cyclonedds/src/tools/ddsperf/cputime.c
Thijs Sassen c0b0db3ae2 Fixed not building freertos target due to conflict in print definition
Signed-off-by: Thijs Sassen <thijs.sassen@adlinktech.com>
2019-08-05 13:56:53 +02:00

250 lines
7.2 KiB
C

/*
* Copyright(c) 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#define _ISOC99_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "dds/dds.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/sockets.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/rusage.h"
#include "cputime.h"
#include "ddsperf_types.h"
static void print_one (char *line, size_t sz, size_t *pos, const char *name, double du, double ds)
{
if (*pos < sz)
*pos += (size_t) snprintf (line + *pos, sz - *pos, " %s:%.0f%%+%.0f%%", name, 100.0 * du, 100.0 * ds);
}
bool print_cputime (const struct CPUStats *s, const char *prefix, bool print_host, bool is_fresh)
{
if (!s->some_above)
return false;
else
{
char line[512];
size_t pos = 0;
assert (is_fresh || !print_host);
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, "%s", prefix);
if (!is_fresh)
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " (stale)");
if (print_host)
{
int n = (int) strlen (s->hostname);
if (n > 100) n = 100;
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " @%*.*s:%"PRId32, n, n, s->hostname, s->pid);
}
if (s->maxrss > 1048576)
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " rss:%.1fMB", s->maxrss / 1048576.0);
else if (s->maxrss > 1024)
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " rss:%.0fkB", s->maxrss / 1024.0);
else {
/* non-sensical value -- presumably maxrss is not available */
}
const size_t init_pos = pos;
for (uint32_t i = 0; i < s->cpu._length; i++)
{
struct CPUStatThread * const thr = &s->cpu._buffer[i];
print_one (line, sizeof (line), &pos, thr->name, thr->u_pct / 100.0, thr->s_pct / 100.0);
}
if (pos > init_pos)
puts (line);
return true;
}
}
#if DDSRT_HAVE_RUSAGE && DDSRT_HAVE_THREAD_LIST
struct record_cputime_state_thr {
ddsrt_thread_list_id_t tid;
char name[32];
double ut, st;
};
struct record_cputime_state {
bool supported;
dds_time_t tprev;
size_t nthreads;
struct record_cputime_state_thr *threads;
dds_entity_t wr;
struct CPUStats s;
};
static void update (double *ut_old, double *st_old, double dt, double ut_new, double st_new, double *du, double *ds)
{
*du = (ut_new - *ut_old) / dt;
*ds = (st_new - *st_old) / dt;
*ut_old = ut_new;
*st_old = st_new;
}
static bool above_threshold (double *max, double *du_skip, double *ds_skip, double du, double ds)
{
if (*max < du) *max = du;
if (*max < ds) *max = ds;
if (du >= 0.005 || ds >= 0.005)
return true;
else if (du_skip == NULL || ds_skip == NULL)
return false;
else
{
*du_skip += du;
*ds_skip += ds;
return false;
}
}
bool record_cputime (struct record_cputime_state *state, const char *prefix, dds_time_t tnow)
{
if (state == NULL)
return false;
ddsrt_rusage_t usage;
if (ddsrt_getrusage (DDSRT_RUSAGE_SELF, &usage) < 0)
usage.maxrss = 0;
double max = 0;
double du_skip = 0.0, ds_skip = 0.0;
const double dt = (double) (tnow - state->tprev) / 1e9;
bool some_above = false;
state->s.maxrss = (double) usage.maxrss;
state->s.cpu._length = 0;
for (size_t i = 0; i < state->nthreads; i++)
{
struct record_cputime_state_thr * const thr = &state->threads[i];
if (ddsrt_getrusage_anythread (thr->tid, &usage) < 0)
continue;
const double ut = (double) usage.utime / 1e9;
const double st = (double) usage.stime / 1e9;
double du, ds;
update (&thr->ut, &thr->st, dt, ut, st, &du, &ds);
if (above_threshold (&max, &du_skip, &ds_skip, du, ds))
{
some_above = true;
/* Thread names are often set by thread itself immediately after creation,
and so it depends on the scheduling whether there is still a default
name or the name we are interested in. Lazily retrieving the name the
first time the thread pops up in the CPU usage works around the timing
problem. */
if (thr->name[0] == 0)
{
if (ddsrt_thread_getname_anythread (thr->tid, thr->name, sizeof (thr->name)) < 0)
{
du_skip += du;
ds_skip += ds;
continue;
}
}
struct CPUStatThread * const x = &state->s.cpu._buffer[state->s.cpu._length++];
x->name = thr->name;
x->u_pct = (int) (100.0 * du + 0.5);
x->s_pct = (int) (100.0 * ds + 0.5);
}
}
if (above_threshold (&max, NULL, NULL, du_skip, ds_skip))
{
struct CPUStatThread * const x = &state->s.cpu._buffer[state->s.cpu._length++];
some_above = true;
x->name = "others";
x->u_pct = (int) (100.0 * du_skip + 0.5);
x->s_pct = (int) (100.0 * ds_skip + 0.5);
}
state->tprev = tnow;
state->s.some_above = some_above;
dds_write (state->wr, &state->s);
return print_cputime (&state->s, prefix, false, true);
}
struct record_cputime_state *record_cputime_new (dds_entity_t wr)
{
ddsrt_thread_list_id_t tids[100];
dds_return_t n;
if ((n = ddsrt_thread_list (tids, sizeof (tids) / sizeof (tids[0]))) <= 0)
return NULL;
else if (n > (dds_return_t) (sizeof (tids) / sizeof (tids[0])))
{
fprintf (stderr, "way more threads than expected\n");
return NULL;
}
struct record_cputime_state *state = malloc (sizeof (*state));
state->tprev = dds_time ();
state->wr = wr;
state->threads = malloc ((size_t) n * sizeof (*state->threads));
state->nthreads = 0;
for (int32_t i = 0; i < n; i++)
{
struct record_cputime_state_thr * const thr = &state->threads[state->nthreads];
ddsrt_rusage_t usage;
if (ddsrt_getrusage_anythread (tids[i], &usage) < 0)
continue;
thr->tid = tids[i];
thr->name[0] = 0;
thr->ut = (double) usage.utime / 1e9;
thr->st = (double) usage.stime / 1e9;
state->nthreads++;
}
char hostname[128];
if (ddsrt_gethostname (hostname, sizeof (hostname)) != DDS_RETCODE_OK)
strcpy (hostname, "?");
state->s.hostname = ddsrt_strdup (hostname);
state->s.pid = (uint32_t) ddsrt_getpid ();
state->s.cpu._length = 0;
state->s.cpu._maximum = (uint32_t) state->nthreads;
state->s.cpu._buffer = malloc (state->s.cpu._maximum * sizeof (*state->s.cpu._buffer));
state->s.cpu._release = false;
return state;
}
void record_cputime_free (struct record_cputime_state *state)
{
if (state)
{
free (state->threads);
ddsrt_free (state->s.hostname);
/* we alias thread names in state->s->cpu._buffer, so no need to free */
free (state->s.cpu._buffer);
free (state);
}
}
#else
bool record_cputime (struct record_cputime_state *state, const char *prefix, dds_time_t tnow)
{
(void) state;
(void) prefix;
(void) tnow;
}
struct record_cputime_state *record_cputime_new (dds_entity_t wr)
{
(void) wr;
}
void record_cputime_free (struct record_cputime_state *state)
{
(void) state;
}
#endif