Add gathering per-thread CPU usage to ddsrt
Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
parent
9b1920862e
commit
f9808c7656
10 changed files with 589 additions and 109 deletions
|
@ -22,12 +22,15 @@
|
|||
# else
|
||||
# define DDSRT_HAVE_RUSAGE 0
|
||||
#endif
|
||||
#else
|
||||
#elif defined (_WIN32) || defined (__linux) || defined (__APPLE__)
|
||||
# define DDSRT_HAVE_RUSAGE 1
|
||||
#else
|
||||
# define DDSRT_HAVE_RUSAGE 0
|
||||
#endif
|
||||
|
||||
#include "dds/ddsrt/time.h"
|
||||
#include "dds/ddsrt/retcode.h"
|
||||
#include "dds/ddsrt/threads.h"
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
|
@ -43,8 +46,10 @@ typedef struct {
|
|||
size_t nivcsw; /* Involuntary context switches. Not maintained on Windows. */
|
||||
} ddsrt_rusage_t;
|
||||
|
||||
#define DDSRT_RUSAGE_SELF (0)
|
||||
#define DDSRT_RUSAGE_THREAD (1)
|
||||
enum ddsrt_getrusage_who {
|
||||
DDSRT_RUSAGE_SELF,
|
||||
DDSRT_RUSAGE_THREAD
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get resource usage for the current thread or process.
|
||||
|
@ -61,7 +66,26 @@ typedef struct {
|
|||
* @retval DDS_RETCODE_ERROR
|
||||
* An unidentified error occurred.
|
||||
*/
|
||||
DDS_EXPORT dds_return_t ddsrt_getrusage(int who, ddsrt_rusage_t *usage);
|
||||
DDS_EXPORT dds_return_t ddsrt_getrusage(enum ddsrt_getrusage_who who, ddsrt_rusage_t *usage);
|
||||
|
||||
#if DDSRT_HAVE_THREAD_LIST
|
||||
/**
|
||||
* @brief Get resource usage for some thread.
|
||||
*
|
||||
* @param[in] tid id of the thread of to get the resource usage for
|
||||
* @param[in] usage Structure where resource usage is returned.
|
||||
*
|
||||
* @returns A dds_return_t indicating success or failure.
|
||||
*
|
||||
* @retval DDS_RETCODE_OK
|
||||
* Resource usage successfully returned in @usage.
|
||||
* @retval DDS_RETCODE_OUT_OF_RESOURCES
|
||||
* There were not enough resources to get resource usage.
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* An unidentified error occurred.
|
||||
*/
|
||||
DDS_EXPORT dds_return_t ddsrt_getrusage_anythread (ddsrt_thread_list_id_t tid, ddsrt_rusage_t * __restrict usage);
|
||||
#endif
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
|
|
|
@ -214,6 +214,53 @@ ddsrt_thread_setname(
|
|||
const char *__restrict name);
|
||||
#endif
|
||||
|
||||
#if DDSRT_HAVE_THREAD_LIST
|
||||
/**
|
||||
* @brief Get a list of threads in the calling process
|
||||
*
|
||||
* @param[out] tids Array of size elements to be filled with thread
|
||||
* identifiers, may be NULL if size is 0
|
||||
* @param[in] size The size of the tids array; 0 is allowed
|
||||
*
|
||||
* @returns A dds_return_t indicating the number of threads in the process
|
||||
* or an error code on failure.
|
||||
*
|
||||
* @retval > 0
|
||||
* Number of threads in the process, may be larger than size
|
||||
* tids[0 .. (return - 1)] are valid
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* Something went wrong, contents of tids is undefined
|
||||
* @retval DDS_RETCODE_UNSUPPORTED
|
||||
* Not supported on the platform
|
||||
*/
|
||||
DDS_EXPORT dds_return_t ddsrt_thread_list (ddsrt_thread_list_id_t * __restrict tids, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Get the name of the specified thread (in the calling process)
|
||||
*
|
||||
* @param[in] tid Thread identifier for which the name is sought
|
||||
* @param[out] name Filled with the thread name (or a synthesized one)
|
||||
* on successful return; name is silently truncated
|
||||
* if the actual name is longer than name can hold;
|
||||
* always 0-terminated if size > 0
|
||||
* @param[in] size Number of bytes of name that may be assigned, size
|
||||
* is 0 is allowed, though somewhat useless
|
||||
*
|
||||
* @returns A dds_return_t indicating success or failure.
|
||||
*
|
||||
* @retval DDS_RETCODE_OK
|
||||
* Possibly truncated name is returned as a null-terminated
|
||||
* string in name (provided size > 0).
|
||||
* @retval DDS_RETCODE_NOT_FOUND
|
||||
* Thread not found; the contents of name is unchanged
|
||||
* @retval DDS_RETCODE_ERROR
|
||||
* Unspecified failure, the contents of name is undefined
|
||||
* @retval DDS_RETCODE_UNSUPPORTED
|
||||
* Not supported on the platform
|
||||
*/
|
||||
DDS_EXPORT dds_return_t ddsrt_thread_getname_anythread (ddsrt_thread_list_id_t tid, char *__restrict name, size_t size);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Push cleanup handler onto the cleanup stack
|
||||
*
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <task.h>
|
||||
|
||||
#define DDSRT_HAVE_THREAD_SETNAME (0)
|
||||
#define DDSRT_HAVE_THREAD_LIST (0)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
#else
|
||||
#define DDSRT_HAVE_THREAD_SETNAME (1)
|
||||
#endif
|
||||
#if defined (__linux) || defined (__APPLE__)
|
||||
#define DDSRT_HAVE_THREAD_LIST (1)
|
||||
#else
|
||||
#define DDSRT_HAVE_THREAD_LIST (0)
|
||||
#endif
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
|
@ -27,6 +32,7 @@ extern "C" {
|
|||
#if defined(__linux)
|
||||
typedef long int ddsrt_tid_t;
|
||||
#define PRIdTID "ld"
|
||||
typedef long int ddsrt_thread_list_id_t;
|
||||
/* __linux */
|
||||
#elif defined(__FreeBSD__) && (__FreeBSD_version >= 900031)
|
||||
/* FreeBSD >= 9.0 */
|
||||
|
@ -38,6 +44,8 @@ typedef int ddsrt_tid_t;
|
|||
/* macOS X >= 10.6 */
|
||||
typedef uint64_t ddsrt_tid_t;
|
||||
#define PRIdTID PRIu64
|
||||
/* ddsrt_thread_list_id_t is actually a mach_port_t */
|
||||
typedef uint32_t ddsrt_thread_list_id_t;
|
||||
/* __APPLE__ */
|
||||
#elif defined(__VXWORKS__)
|
||||
/* TODO: Verify taskIdSelf is the right function to use on VxWorks */
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "dds/ddsrt/types.h"
|
||||
|
||||
#define DDSRT_HAVE_THREAD_SETNAME (1)
|
||||
#define DDSRT_HAVE_THREAD_LIST (1)
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
|
@ -28,6 +29,8 @@ typedef struct {
|
|||
typedef DWORD ddsrt_tid_t;
|
||||
#define PRIdTID "u"
|
||||
|
||||
typedef HANDLE ddsrt_thread_list_id_t;
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -78,8 +78,18 @@ rusage_thread(ddsrt_rusage_t *usage)
|
|||
return DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
#if ! DDSRT_HAVE_THREAD_LIST
|
||||
static
|
||||
#endif
|
||||
dds_return_t
|
||||
ddsrt_getrusage(int who, ddsrt_rusage_t *usage)
|
||||
ddsrt_getrusage_anythread(ddsrt_thread_list_id_t tid, ddsrt_rusage_t *__restrict usage)
|
||||
{
|
||||
assert(usage != NULL);
|
||||
return rusage_thread(tid, usage);
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_getrusage(enum ddsrt_getrusage_who who, ddsrt_rusage_t *usage)
|
||||
{
|
||||
dds_return_t rc;
|
||||
|
||||
|
@ -87,7 +97,7 @@ ddsrt_getrusage(int who, ddsrt_rusage_t *usage)
|
|||
assert(usage != NULL);
|
||||
|
||||
if (who == DDSRT_RUSAGE_THREAD) {
|
||||
rc = rusage_thread(usage);
|
||||
rc = rusage_thread_anythread(xTaskGetCurrentTaskHandle(), usage);
|
||||
} else {
|
||||
rc = rusage_self(usage);
|
||||
}
|
||||
|
|
|
@ -15,87 +15,212 @@
|
|||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "dds/ddsrt/rusage.h"
|
||||
|
||||
#if defined __linux
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
|
||||
dds_return_t
|
||||
ddsrt_getrusage_anythread (
|
||||
ddsrt_thread_list_id_t tid,
|
||||
ddsrt_rusage_t * __restrict usage)
|
||||
{
|
||||
/* Linux' man pages happily state that the second field is the process/task name
|
||||
in parentheses, and that %s is the correct scanf conversion. As it turns out
|
||||
the process name itself can contain spaces and parentheses ... so %s is not a
|
||||
good choice for the general case. The others are spec'd as a character or a
|
||||
number, which suggests the correct procedure is to have the 2nd field start at
|
||||
the first ( and end at the last ) ...
|
||||
|
||||
RSS is per-process, so no point in populating that one
|
||||
field 14, 15: utime, stime (field 1 is first)
|
||||
|
||||
Voluntary and involuntary context switches can be found in .../status, but
|
||||
not in stat; and .../status does not give the time. Crazy. */
|
||||
const double hz = (double) sysconf (_SC_CLK_TCK);
|
||||
char file[100];
|
||||
FILE *fp;
|
||||
int pos;
|
||||
pos = snprintf (file, sizeof (file), "/proc/self/task/%lu/stat", (unsigned long) tid);
|
||||
if (pos < 0 || pos >= (int) sizeof (file))
|
||||
return DDS_RETCODE_ERROR;
|
||||
if ((fp = fopen (file, "r")) == NULL)
|
||||
return DDS_RETCODE_NOT_FOUND;
|
||||
/* max 2 64-bit ints plus some whitespace; need 1 extra for detecting something
|
||||
went wrong and we ended up gobbling up garbage; 64 will do */
|
||||
char save[64];
|
||||
size_t savepos = 0;
|
||||
int prevc, c;
|
||||
int field = 1;
|
||||
for (prevc = 0; (c = fgetc (fp)) != EOF; prevc = c)
|
||||
{
|
||||
if (field == 1)
|
||||
{
|
||||
if (c == '(')
|
||||
field = 2;
|
||||
}
|
||||
else if (field >= 2)
|
||||
{
|
||||
/* each close paren resets the field counter to 3 (the first is common,
|
||||
further ones are rare and occur only if the thread name contains a
|
||||
closing parenthesis), as well as the save space for fields 14 & 15
|
||||
that we care about. */
|
||||
if (c == ')')
|
||||
{
|
||||
field = 2;
|
||||
savepos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* next field on transition of whitespace to non-whitespace */
|
||||
if (c != ' ' && prevc == ' ')
|
||||
field++;
|
||||
/* save fields 14 & 15 while continuing scanning to EOF on the off-chance
|
||||
that 14&15 initially appear to be in what ultimately turns out to be
|
||||
task name */
|
||||
if (field == 14 || field == 15)
|
||||
{
|
||||
if (savepos < sizeof (save) - 1)
|
||||
save[savepos++] = (char) c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose (fp);
|
||||
assert (savepos < sizeof (save));
|
||||
save[savepos] = 0;
|
||||
if (savepos == sizeof (save) - 1)
|
||||
return DDS_RETCODE_ERROR;
|
||||
/* it's really integer, but the conversion from an unknown HZ value is much
|
||||
less tricky in floating-point */
|
||||
double user, sys;
|
||||
if (sscanf (save, "%lf %lf%n", &user, &sys, &pos) != 2 || (save[pos] != 0 && save[pos] != ' '))
|
||||
return DDS_RETCODE_ERROR;
|
||||
usage->utime = (dds_time_t) (1e9 * user / hz);
|
||||
usage->stime = (dds_time_t) (1e9 * sys / hz);
|
||||
usage->idrss = 0;
|
||||
usage->maxrss = 0;
|
||||
usage->nvcsw = 0;
|
||||
usage->nivcsw = 0;
|
||||
|
||||
pos = snprintf (file, sizeof (file), "/proc/self/task/%lu/status", (unsigned long) tid);
|
||||
if (pos < 0 || pos >= (int) sizeof (file))
|
||||
return DDS_RETCODE_ERROR;
|
||||
if ((fp = fopen (file, "r")) == NULL)
|
||||
return DDS_RETCODE_NOT_FOUND;
|
||||
enum { ERROR, READ_HEADING, SKIP_TO_EOL, READ_VCSW, READ_IVCSW } state = READ_HEADING;
|
||||
savepos = 0;
|
||||
while (state != ERROR && (c = fgetc (fp)) != EOF)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case READ_HEADING:
|
||||
if (savepos < sizeof (save) - 1)
|
||||
save[savepos++] = (char) c;
|
||||
if (c == ':')
|
||||
{
|
||||
save[savepos] = 0;
|
||||
savepos = 0;
|
||||
if (strcmp (save, "voluntary_ctxt_switches:") == 0)
|
||||
state = READ_VCSW;
|
||||
else if (strcmp (save, "nonvoluntary_ctxt_switches:") == 0)
|
||||
state = READ_IVCSW;
|
||||
else
|
||||
state = SKIP_TO_EOL;
|
||||
}
|
||||
break;
|
||||
case SKIP_TO_EOL:
|
||||
if (c == '\n')
|
||||
state = READ_HEADING;
|
||||
break;
|
||||
case READ_VCSW:
|
||||
case READ_IVCSW:
|
||||
if (fscanf (fp, "%zu", (state == READ_VCSW) ? &usage->nvcsw : &usage->nivcsw) != 1)
|
||||
state = ERROR;
|
||||
else
|
||||
state = SKIP_TO_EOL;
|
||||
break;
|
||||
case ERROR:
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose (fp);
|
||||
return (state == ERROR) ? DDS_RETCODE_ERROR : DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_getrusage (enum ddsrt_getrusage_who who, ddsrt_rusage_t *usage)
|
||||
{
|
||||
struct rusage buf;
|
||||
|
||||
assert (who == DDSRT_RUSAGE_SELF || who == DDSRT_RUSAGE_THREAD);
|
||||
assert (usage != NULL);
|
||||
|
||||
memset (&buf, 0, sizeof(buf));
|
||||
if (getrusage ((who == DDSRT_RUSAGE_SELF) ? RUSAGE_SELF : RUSAGE_THREAD, &buf) == -1)
|
||||
return DDS_RETCODE_ERROR;
|
||||
|
||||
usage->utime = (buf.ru_utime.tv_sec * DDS_NSECS_IN_SEC) + (buf.ru_utime.tv_usec * DDS_NSECS_IN_USEC);
|
||||
usage->stime = (buf.ru_stime.tv_sec * DDS_NSECS_IN_SEC) + (buf.ru_stime.tv_usec * DDS_NSECS_IN_USEC);
|
||||
usage->maxrss = 1024 * (size_t) buf.ru_maxrss;
|
||||
usage->idrss = (size_t) buf.ru_idrss;
|
||||
usage->nvcsw = (size_t) buf.ru_nvcsw;
|
||||
usage->nivcsw = (size_t) buf.ru_nivcsw;
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
#elif defined (__APPLE__)
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/thread_act.h>
|
||||
#endif
|
||||
|
||||
#include "dds/ddsrt/rusage.h"
|
||||
|
||||
dds_return_t
|
||||
ddsrt_getrusage(int who, ddsrt_rusage_t *usage)
|
||||
ddsrt_getrusage_anythread (
|
||||
ddsrt_thread_list_id_t tid,
|
||||
ddsrt_rusage_t * __restrict usage)
|
||||
{
|
||||
mach_msg_type_number_t cnt;
|
||||
thread_basic_info_data_t info;
|
||||
cnt = THREAD_BASIC_INFO_COUNT;
|
||||
if (thread_info ((mach_port_t) tid, THREAD_BASIC_INFO, (thread_info_t) &info, &cnt) != KERN_SUCCESS)
|
||||
return DDS_RETCODE_ERROR;
|
||||
|
||||
/* Don't see an (easy) way to get context switch counts */
|
||||
usage->utime = info.user_time.seconds * DDS_NSECS_IN_SEC + info.user_time.microseconds * DDS_NSECS_IN_USEC;
|
||||
usage->stime = info.system_time.seconds * DDS_NSECS_IN_SEC + info.system_time.microseconds * DDS_NSECS_IN_USEC;
|
||||
usage->idrss = 0;
|
||||
usage->maxrss = 0;
|
||||
usage->nivcsw = 0;
|
||||
usage->nvcsw = 0;
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_getrusage (enum ddsrt_getrusage_who who, ddsrt_rusage_t *usage)
|
||||
{
|
||||
int err = 0;
|
||||
struct rusage buf;
|
||||
dds_return_t rc;
|
||||
|
||||
assert(who == DDSRT_RUSAGE_SELF || who == DDSRT_RUSAGE_THREAD);
|
||||
assert(usage != NULL);
|
||||
assert (usage != NULL);
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
memset (&buf, 0, sizeof(buf));
|
||||
if (getrusage (RUSAGE_SELF, &buf) == -1)
|
||||
return DDS_RETCODE_ERROR;
|
||||
|
||||
#if defined(__linux)
|
||||
if ((who == DDSRT_RUSAGE_SELF && getrusage(RUSAGE_SELF, &buf) == -1) ||
|
||||
(who == DDSRT_RUSAGE_THREAD && getrusage(RUSAGE_THREAD, &buf) == -1))
|
||||
{
|
||||
err = errno;
|
||||
} else {
|
||||
buf.ru_maxrss *= 1024;
|
||||
switch (who) {
|
||||
case DDSRT_RUSAGE_THREAD:
|
||||
if ((rc = ddsrt_getrusage_anythread (pthread_mach_thread_np (pthread_self()), usage)) < 0)
|
||||
return rc;
|
||||
break;
|
||||
case DDSRT_RUSAGE_SELF:
|
||||
usage->utime = (buf.ru_utime.tv_sec * DDS_NSECS_IN_SEC) + (buf.ru_utime.tv_usec * DDS_NSECS_IN_USEC);
|
||||
usage->stime = (buf.ru_stime.tv_sec * DDS_NSECS_IN_SEC) + (buf.ru_stime.tv_usec * DDS_NSECS_IN_USEC);
|
||||
usage->nvcsw = (size_t) buf.ru_nvcsw;
|
||||
usage->nivcsw = (size_t) buf.ru_nivcsw;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (getrusage(RUSAGE_SELF, &buf) == -1) {
|
||||
err = errno;
|
||||
} else if (who == DDSRT_RUSAGE_THREAD) {
|
||||
memset(&buf.ru_utime, 0, sizeof(buf.ru_utime));
|
||||
memset(&buf.ru_stime, 0, sizeof(buf.ru_stime));
|
||||
buf.ru_nvcsw = 0;
|
||||
buf.ru_nivcsw = 0;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
kern_return_t ret;
|
||||
mach_port_t thr;
|
||||
mach_msg_type_number_t cnt;
|
||||
thread_basic_info_data_t info;
|
||||
|
||||
thr = mach_thread_self();
|
||||
assert(thr != MACH_PORT_DEAD);
|
||||
if (thr == MACH_PORT_NULL) {
|
||||
/* Resource shortage prevented reception of send right. */
|
||||
err = ENOMEM;
|
||||
} else {
|
||||
cnt = THREAD_BASIC_INFO_COUNT;
|
||||
ret = thread_info(
|
||||
thr, THREAD_BASIC_INFO, (thread_info_t)&info, &cnt);
|
||||
assert(ret != KERN_INVALID_ARGUMENT);
|
||||
/* Assume MIG_ARRAY_TOO_LARGE will not happen. */
|
||||
buf.ru_utime.tv_sec = info.user_time.seconds;
|
||||
buf.ru_utime.tv_usec = info.user_time.microseconds;
|
||||
buf.ru_stime.tv_sec = info.system_time.seconds;
|
||||
buf.ru_stime.tv_usec = info.system_time.microseconds;
|
||||
mach_port_deallocate(mach_task_self(), thr);
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
}
|
||||
#endif /* __linux */
|
||||
|
||||
if (err == 0) {
|
||||
rc = DDS_RETCODE_OK;
|
||||
usage->utime =
|
||||
(buf.ru_utime.tv_sec * DDS_NSECS_IN_SEC) +
|
||||
(buf.ru_utime.tv_usec * DDS_NSECS_IN_USEC);
|
||||
usage->stime =
|
||||
(buf.ru_stime.tv_sec * DDS_NSECS_IN_SEC) +
|
||||
(buf.ru_stime.tv_usec * DDS_NSECS_IN_USEC);
|
||||
usage->maxrss = (size_t)buf.ru_maxrss;
|
||||
usage->idrss = (size_t)buf.ru_idrss;
|
||||
usage->nvcsw = (size_t)buf.ru_nvcsw;
|
||||
usage->nivcsw = (size_t)buf.ru_nivcsw;
|
||||
} else if (err == ENOMEM) {
|
||||
rc = DDS_RETCODE_OUT_OF_RESOURCES;
|
||||
} else {
|
||||
rc = DDS_RETCODE_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
usage->maxrss = (size_t) buf.ru_maxrss;
|
||||
usage->idrss = (size_t) buf.ru_idrss;
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -18,36 +18,60 @@
|
|||
#include <psapi.h>
|
||||
|
||||
dds_time_t
|
||||
filetime_to_time(const FILETIME *ft)
|
||||
filetime_to_time (const FILETIME *ft)
|
||||
{
|
||||
/* FILETIME structures express times in 100-nanosecond time units. */
|
||||
return ((ft->dwHighDateTime << 31) + (ft->dwLowDateTime)) * 100;
|
||||
return (dds_time_t) ((((uint64_t) ft->dwHighDateTime << 31) + (ft->dwLowDateTime)) * 100);
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_getrusage(int who, ddsrt_rusage_t *usage)
|
||||
ddsrt_getrusage_anythread (ddsrt_thread_list_id_t tid, ddsrt_rusage_t * __restrict usage)
|
||||
{
|
||||
FILETIME stime, utime, ctime, etime;
|
||||
PROCESS_MEMORY_COUNTERS pmctrs;
|
||||
|
||||
assert(who == DDSRT_RUSAGE_SELF || who == DDSRT_RUSAGE_THREAD);
|
||||
assert(usage != NULL);
|
||||
assert (usage != NULL);
|
||||
|
||||
/* Memory counters are per process, but populate them if thread resource
|
||||
usage is requested to keep in sync with Linux. */
|
||||
if ((!GetProcessMemoryInfo(GetCurrentProcess(), &pmctrs, sizeof(pmctrs)))
|
||||
|| (who == DDSRT_RUSAGE_SELF &&
|
||||
!GetProcessTimes(GetCurrentProcess(), &ctime, &etime, &stime, &utime))
|
||||
|| (who == DDSRT_RUSAGE_THREAD &&
|
||||
!GetThreadTimes(GetCurrentThread(), &ctime, &etime, &stime, &utime)))
|
||||
{
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
memset(usage, 0, sizeof(*usage));
|
||||
usage->stime = filetime_to_time(&stime);
|
||||
usage->utime = filetime_to_time(&utime);
|
||||
usage->maxrss = pmctrs.PeakWorkingSetSize;
|
||||
if (!GetThreadTimes (tid, &ctime, &etime, &stime, &utime))
|
||||
return DDS_RETCODE_ERROR;
|
||||
|
||||
memset (usage, 0, sizeof (*usage));
|
||||
usage->stime = filetime_to_time (&stime);
|
||||
usage->utime = filetime_to_time (&utime);
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_getrusage (enum ddsrt_getrusage_who who, ddsrt_rusage_t *usage)
|
||||
{
|
||||
PROCESS_MEMORY_COUNTERS pmctrs;
|
||||
|
||||
assert (who == DDSRT_RUSAGE_SELF || who == DDSRT_RUSAGE_THREAD);
|
||||
assert (usage != NULL);
|
||||
|
||||
/* Memory counters are per process, but populate them if thread resource
|
||||
usage is requested to keep in sync with Linux. */
|
||||
if (!GetProcessMemoryInfo (GetCurrentProcess (), &pmctrs, sizeof (pmctrs)))
|
||||
return DDS_RETCODE_ERROR;
|
||||
|
||||
if (who == DDSRT_RUSAGE_THREAD)
|
||||
{
|
||||
dds_return_t rc;
|
||||
if ((rc = ddsrt_getrusage_anythread (GetCurrentThread (), usage)) < 0)
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
FILETIME stime, utime, ctime, etime;
|
||||
if (!GetProcessTimes (GetCurrentProcess (), &ctime, &etime, &stime, &utime))
|
||||
return DDS_RETCODE_ERROR;
|
||||
memset(usage, 0, sizeof(*usage));
|
||||
usage->stime = filetime_to_time(&stime);
|
||||
usage->utime = filetime_to_time(&utime);
|
||||
}
|
||||
|
||||
usage->maxrss = pmctrs.PeakWorkingSetSize;
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "dds/ddsrt/string.h"
|
||||
#include "dds/ddsrt/threads_priv.h"
|
||||
#include "dds/ddsrt/types.h"
|
||||
#include "dds/ddsrt/static_assert.h"
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
|
@ -39,9 +40,14 @@ typedef struct {
|
|||
|
||||
#if defined(__linux)
|
||||
#include <sys/syscall.h>
|
||||
#include <dirent.h>
|
||||
#define MAXTHREADNAMESIZE (15) /* 16 bytes including null-terminating byte. */
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/thread_info.h> /* MAXTHREADNAMESIZE */
|
||||
#include <mach/task.h>
|
||||
#include <mach/task_info.h>
|
||||
#include <mach/vm_map.h>
|
||||
#elif defined(__sun)
|
||||
#define MAXTHREADNAMESIZE (31)
|
||||
#elif defined(__FreeBSD__)
|
||||
|
@ -363,7 +369,7 @@ ddsrt_thread_join(ddsrt_thread_t thread, uint32_t *thread_result)
|
|||
|
||||
if ((err = pthread_join (thread.v, &vthread_result)) != 0)
|
||||
{
|
||||
DDS_TRACE ("pthread_join(0x%"PRIxMAX") failed with error %d\n", (uintmax_t)((uintptr_t)thread.v), err);
|
||||
DDS_ERROR ("pthread_join(0x%"PRIxMAX") failed with error %d\n", (uintmax_t)((uintptr_t)thread.v), err);
|
||||
return DDS_RETCODE_ERROR;
|
||||
}
|
||||
|
||||
|
@ -372,6 +378,104 @@ ddsrt_thread_join(ddsrt_thread_t thread, uint32_t *thread_result)
|
|||
return DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
#if defined __linux
|
||||
dds_return_t
|
||||
ddsrt_thread_list (
|
||||
ddsrt_thread_list_id_t * __restrict tids,
|
||||
size_t size)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
if ((dir = opendir ("/proc/self/task")) == NULL)
|
||||
return DDS_RETCODE_ERROR;
|
||||
dds_return_t n = 0;
|
||||
while ((de = readdir (dir)) != NULL)
|
||||
{
|
||||
if (de->d_name[0] == '.' && (de->d_name[1] == 0 || (de->d_name[1] == '.' && de->d_name[2] == 0)))
|
||||
continue;
|
||||
int pos;
|
||||
long tid;
|
||||
if (sscanf (de->d_name, "%ld%n", &tid, &pos) != 1 || de->d_name[pos] != 0)
|
||||
{
|
||||
n = DDS_RETCODE_ERROR;
|
||||
break;
|
||||
}
|
||||
if ((size_t) n < size)
|
||||
tids[n] = (ddsrt_thread_list_id_t) tid;
|
||||
n++;
|
||||
}
|
||||
closedir (dir);
|
||||
/* If there were no threads, something must've gone badly wrong */
|
||||
return (n == 0) ? DDS_RETCODE_ERROR : n;
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_thread_getname_anythread (
|
||||
ddsrt_thread_list_id_t tid,
|
||||
char *__restrict name,
|
||||
size_t size)
|
||||
{
|
||||
char file[100];
|
||||
FILE *fp;
|
||||
int pos;
|
||||
pos = snprintf (file, sizeof (file), "/proc/self/task/%lu/stat", (unsigned long) tid);
|
||||
if (pos < 0 || pos >= (int) sizeof (file))
|
||||
return DDS_RETCODE_ERROR;
|
||||
if ((fp = fopen (file, "r")) == NULL)
|
||||
return DDS_RETCODE_NOT_FOUND;
|
||||
int c;
|
||||
size_t namelen = 0, namepos = 0;
|
||||
while ((c = fgetc (fp)) != EOF)
|
||||
if (c == '(')
|
||||
break;
|
||||
while ((c = fgetc (fp)) != EOF)
|
||||
{
|
||||
if (c == ')')
|
||||
namelen = namepos;
|
||||
if (namepos + 1 < size)
|
||||
name[namepos++] = (char) c;
|
||||
}
|
||||
fclose (fp);
|
||||
assert (size == 0 || namelen < size);
|
||||
if (size > 0)
|
||||
name[namelen] = 0;
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
#elif defined __APPLE__
|
||||
DDSRT_STATIC_ASSERT (sizeof (ddsrt_thread_list_id_t) == sizeof (mach_port_t));
|
||||
|
||||
dds_return_t
|
||||
ddsrt_thread_list (
|
||||
ddsrt_thread_list_id_t * __restrict tids,
|
||||
size_t size)
|
||||
{
|
||||
thread_act_array_t tasks;
|
||||
mach_msg_type_number_t count;
|
||||
if (task_threads (mach_task_self (), &tasks, &count) != KERN_SUCCESS)
|
||||
return DDS_RETCODE_ERROR;
|
||||
for (mach_msg_type_number_t i = 0; i < count && (size_t) i < size; i++)
|
||||
tids[i] = (ddsrt_thread_list_id_t) tasks[i];
|
||||
vm_deallocate (mach_task_self (), (vm_address_t) tasks, count * sizeof (thread_act_t));
|
||||
return (dds_return_t) count;
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_thread_getname_anythread (
|
||||
ddsrt_thread_list_id_t tid,
|
||||
char *__restrict name,
|
||||
size_t size)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
pthread_t pt = pthread_from_mach_thread_np ((mach_port_t) tid);
|
||||
name[0] = '\0';
|
||||
if (pt == NULL || pthread_getname_np (pt, name, size) != 0 || name[0] == 0)
|
||||
snprintf (name, size, "task%"PRIu64, (uint64_t) tid);
|
||||
}
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static pthread_key_t thread_cleanup_key;
|
||||
static pthread_once_t thread_once = PTHREAD_ONCE_INIT;
|
||||
|
|
|
@ -16,6 +16,60 @@
|
|||
#include "dds/ddsrt/string.h"
|
||||
#include "dds/ddsrt/threads_priv.h"
|
||||
|
||||
/* tlhelp32 for ddsrt_thread_list */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
/* {Get,Set}ThreadDescription is the Windows 10 interface for dealing with thread names, but it at
|
||||
least in some setups the linker can't find the symbols in kernel32.lib, even though kernel32.dll
|
||||
exports them. (Perhaps it is just a broken installation, who knows ...) Looking them up
|
||||
dynamically works fine. */
|
||||
typedef HRESULT (WINAPI *SetThreadDescription_t) (HANDLE hThread, PCWSTR lpThreadDescription);
|
||||
typedef HRESULT (WINAPI *GetThreadDescription_t) (HANDLE hThread, PWSTR *ppszThreadDescription);
|
||||
static volatile SetThreadDescription_t SetThreadDescription_ptr = 0;
|
||||
static volatile GetThreadDescription_t GetThreadDescription_ptr = 0;
|
||||
|
||||
static HRESULT WINAPI SetThreadDescription_dummy (HANDLE hThread, PCWSTR lpThreadDescription)
|
||||
{
|
||||
(void) hThread;
|
||||
(void) lpThreadDescription;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI GetThreadDescription_dummy (HANDLE hThread, PWSTR *ppszThreadDescription)
|
||||
{
|
||||
(void) hThread;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
static void getset_threaddescription_addresses (void)
|
||||
{
|
||||
/* Rely on MSVC's interpretation of the meaning of volatile
|
||||
to order checking & setting the pointers */
|
||||
if (GetThreadDescription_ptr == 0)
|
||||
{
|
||||
HMODULE mod;
|
||||
FARPROC p;
|
||||
if (!GetModuleHandleExA (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, "kernel32.dll", &mod))
|
||||
{
|
||||
SetThreadDescription_ptr = SetThreadDescription_dummy;
|
||||
GetThreadDescription_ptr = GetThreadDescription_dummy;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((p = GetProcAddress (mod, "SetThreadDescription")) != 0)
|
||||
SetThreadDescription_ptr = (SetThreadDescription_t) p;
|
||||
else
|
||||
SetThreadDescription_ptr = SetThreadDescription_dummy;
|
||||
if ((p = GetProcAddress (mod, "GetThreadDescription")) != 0)
|
||||
GetThreadDescription_ptr = (GetThreadDescription_t) p;
|
||||
else
|
||||
GetThreadDescription_ptr = GetThreadDescription_dummy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
ddsrt_thread_routine_t routine;
|
||||
|
@ -176,7 +230,10 @@ ddsrt_thread_join(
|
|||
}
|
||||
|
||||
/* Thread names on Linux are limited to 16 bytes, no reason to provide
|
||||
more storage than that as internal threads must adhere to that limit. */
|
||||
more storage than that as internal threads must adhere to that limit.
|
||||
Use the thread-local variable instead of relying on GetThreadDescription
|
||||
to avoid the dynamic memory allocation, as the thread name is used by
|
||||
the logging code and the overhead there matters. */
|
||||
static ddsrt_thread_local char thread_name[16] = "";
|
||||
|
||||
size_t
|
||||
|
@ -197,6 +254,16 @@ ddsrt_thread_getname(
|
|||
return cnt;
|
||||
}
|
||||
|
||||
/** \brief Set thread name for debugging and system monitoring
|
||||
*
|
||||
* Windows 10 introduced the SetThreadDescription function, which is
|
||||
* obviously the sane interface. For reasons unknown to me, the
|
||||
* linker claims to have no knowledge of the function, even though
|
||||
* they appear present, and so it seems to sensible to retain the
|
||||
* old exception-based trick as a fall-back mechanism. At least
|
||||
* until the reason for {Get,Set}Description's absence from the
|
||||
* regular libraries.
|
||||
*/
|
||||
static const DWORD MS_VC_EXCEPTION=0x406D1388;
|
||||
|
||||
#pragma pack(push,8)
|
||||
|
@ -209,19 +276,23 @@ typedef struct tagTHREADNAME_INFO
|
|||
} THREADNAME_INFO;
|
||||
#pragma pack(pop)
|
||||
|
||||
/** \brief Wrap thread start routine
|
||||
*
|
||||
* \b os_startRoutineWrapper wraps a threads starting routine.
|
||||
* before calling the user routine. It tries to set a thread name
|
||||
* that will be visible if the process is running under the MS
|
||||
* debugger.
|
||||
*/
|
||||
void
|
||||
ddsrt_thread_setname(
|
||||
const char *__restrict name)
|
||||
{
|
||||
assert(name != NULL);
|
||||
|
||||
assert (name != NULL);
|
||||
getset_threaddescription_addresses ();
|
||||
if (SetThreadDescription_ptr != SetThreadDescription_dummy)
|
||||
{
|
||||
size_t size = strlen (name) + 1;
|
||||
wchar_t *wname = malloc (size * sizeof (*wname));
|
||||
size_t cnt = 0;
|
||||
mbstowcs_s (&cnt, wname, size, name, _TRUNCATE);
|
||||
SetThreadDescription_ptr (GetCurrentThread (), wname);
|
||||
free (wname);
|
||||
}
|
||||
else
|
||||
{
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
|
@ -241,10 +312,73 @@ ddsrt_thread_setname(
|
|||
/* Suppress warnings. */
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
ddsrt_strlcpy(thread_name, name, sizeof(thread_name));
|
||||
}
|
||||
ddsrt_strlcpy (thread_name, name, sizeof (thread_name));
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_thread_list (
|
||||
ddsrt_thread_list_id_t * __restrict tids,
|
||||
size_t size)
|
||||
{
|
||||
HANDLE hThreadSnap;
|
||||
THREADENTRY32 te32;
|
||||
const DWORD pid = GetCurrentProcessId ();
|
||||
int32_t n = 0;
|
||||
|
||||
if ((hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0)) == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
memset (&te32, 0, sizeof (te32));
|
||||
te32.dwSize = sizeof (THREADENTRY32);
|
||||
if (!Thread32First (hThreadSnap, &te32))
|
||||
{
|
||||
CloseHandle (hThreadSnap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if (te32.th32OwnerProcessID != pid)
|
||||
continue;
|
||||
if ((size_t) n < size)
|
||||
{
|
||||
/* get a handle to the thread, not counting the thread the thread if no such
|
||||
handle is obtainable */
|
||||
if ((tids[n] = OpenThread (THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID)) == NULL)
|
||||
continue;
|
||||
}
|
||||
n++;
|
||||
} while (Thread32Next (hThreadSnap, &te32));
|
||||
CloseHandle (hThreadSnap);
|
||||
return n;
|
||||
}
|
||||
|
||||
dds_return_t
|
||||
ddsrt_thread_getname_anythread (
|
||||
ddsrt_thread_list_id_t tid,
|
||||
char * __restrict name,
|
||||
size_t size)
|
||||
{
|
||||
getset_threaddescription_addresses ();
|
||||
if (size > 0)
|
||||
{
|
||||
PWSTR data;
|
||||
HRESULT hr = GetThreadDescription_ptr (tid, &data);
|
||||
if (! SUCCEEDED (hr))
|
||||
name[0] = 0;
|
||||
else
|
||||
{
|
||||
size_t cnt;
|
||||
wcstombs_s (&cnt, name, size, data, _TRUNCATE);
|
||||
LocalFree (data);
|
||||
}
|
||||
if (name[0] == 0)
|
||||
{
|
||||
snprintf (name, sizeof (name), "%"PRIdTID, GetThreadId (tid));
|
||||
}
|
||||
}
|
||||
return DDS_RETCODE_OK;
|
||||
}
|
||||
|
||||
static ddsrt_thread_local thread_cleanup_t *thread_cleanup = NULL;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue