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
|
# else
|
||||||
# define DDSRT_HAVE_RUSAGE 0
|
# define DDSRT_HAVE_RUSAGE 0
|
||||||
#endif
|
#endif
|
||||||
#else
|
#elif defined (_WIN32) || defined (__linux) || defined (__APPLE__)
|
||||||
# define DDSRT_HAVE_RUSAGE 1
|
# define DDSRT_HAVE_RUSAGE 1
|
||||||
|
#else
|
||||||
|
# define DDSRT_HAVE_RUSAGE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "dds/ddsrt/time.h"
|
#include "dds/ddsrt/time.h"
|
||||||
#include "dds/ddsrt/retcode.h"
|
#include "dds/ddsrt/retcode.h"
|
||||||
|
#include "dds/ddsrt/threads.h"
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
#if defined (__cplusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -43,8 +46,10 @@ typedef struct {
|
||||||
size_t nivcsw; /* Involuntary context switches. Not maintained on Windows. */
|
size_t nivcsw; /* Involuntary context switches. Not maintained on Windows. */
|
||||||
} ddsrt_rusage_t;
|
} ddsrt_rusage_t;
|
||||||
|
|
||||||
#define DDSRT_RUSAGE_SELF (0)
|
enum ddsrt_getrusage_who {
|
||||||
#define DDSRT_RUSAGE_THREAD (1)
|
DDSRT_RUSAGE_SELF,
|
||||||
|
DDSRT_RUSAGE_THREAD
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get resource usage for the current thread or process.
|
* @brief Get resource usage for the current thread or process.
|
||||||
|
@ -61,7 +66,26 @@ typedef struct {
|
||||||
* @retval DDS_RETCODE_ERROR
|
* @retval DDS_RETCODE_ERROR
|
||||||
* An unidentified error occurred.
|
* 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)
|
#if defined (__cplusplus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,6 +214,53 @@ ddsrt_thread_setname(
|
||||||
const char *__restrict name);
|
const char *__restrict name);
|
||||||
#endif
|
#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
|
* @brief Push cleanup handler onto the cleanup stack
|
||||||
*
|
*
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <task.h>
|
#include <task.h>
|
||||||
|
|
||||||
#define DDSRT_HAVE_THREAD_SETNAME (0)
|
#define DDSRT_HAVE_THREAD_SETNAME (0)
|
||||||
|
#define DDSRT_HAVE_THREAD_LIST (0)
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -19,6 +19,11 @@
|
||||||
#else
|
#else
|
||||||
#define DDSRT_HAVE_THREAD_SETNAME (1)
|
#define DDSRT_HAVE_THREAD_SETNAME (1)
|
||||||
#endif
|
#endif
|
||||||
|
#if defined (__linux) || defined (__APPLE__)
|
||||||
|
#define DDSRT_HAVE_THREAD_LIST (1)
|
||||||
|
#else
|
||||||
|
#define DDSRT_HAVE_THREAD_LIST (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
#if defined (__cplusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -27,6 +32,7 @@ extern "C" {
|
||||||
#if defined(__linux)
|
#if defined(__linux)
|
||||||
typedef long int ddsrt_tid_t;
|
typedef long int ddsrt_tid_t;
|
||||||
#define PRIdTID "ld"
|
#define PRIdTID "ld"
|
||||||
|
typedef long int ddsrt_thread_list_id_t;
|
||||||
/* __linux */
|
/* __linux */
|
||||||
#elif defined(__FreeBSD__) && (__FreeBSD_version >= 900031)
|
#elif defined(__FreeBSD__) && (__FreeBSD_version >= 900031)
|
||||||
/* FreeBSD >= 9.0 */
|
/* FreeBSD >= 9.0 */
|
||||||
|
@ -38,6 +44,8 @@ typedef int ddsrt_tid_t;
|
||||||
/* macOS X >= 10.6 */
|
/* macOS X >= 10.6 */
|
||||||
typedef uint64_t ddsrt_tid_t;
|
typedef uint64_t ddsrt_tid_t;
|
||||||
#define PRIdTID PRIu64
|
#define PRIdTID PRIu64
|
||||||
|
/* ddsrt_thread_list_id_t is actually a mach_port_t */
|
||||||
|
typedef uint32_t ddsrt_thread_list_id_t;
|
||||||
/* __APPLE__ */
|
/* __APPLE__ */
|
||||||
#elif defined(__VXWORKS__)
|
#elif defined(__VXWORKS__)
|
||||||
/* TODO: Verify taskIdSelf is the right function to use on VxWorks */
|
/* TODO: Verify taskIdSelf is the right function to use on VxWorks */
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "dds/ddsrt/types.h"
|
#include "dds/ddsrt/types.h"
|
||||||
|
|
||||||
#define DDSRT_HAVE_THREAD_SETNAME (1)
|
#define DDSRT_HAVE_THREAD_SETNAME (1)
|
||||||
|
#define DDSRT_HAVE_THREAD_LIST (1)
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
#if defined (__cplusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -28,6 +29,8 @@ typedef struct {
|
||||||
typedef DWORD ddsrt_tid_t;
|
typedef DWORD ddsrt_tid_t;
|
||||||
#define PRIdTID "u"
|
#define PRIdTID "u"
|
||||||
|
|
||||||
|
typedef HANDLE ddsrt_thread_list_id_t;
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
#if defined (__cplusplus)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -78,8 +78,18 @@ rusage_thread(ddsrt_rusage_t *usage)
|
||||||
return DDS_RETCODE_OK;
|
return DDS_RETCODE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ! DDSRT_HAVE_THREAD_LIST
|
||||||
|
static
|
||||||
|
#endif
|
||||||
dds_return_t
|
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;
|
dds_return_t rc;
|
||||||
|
|
||||||
|
@ -87,7 +97,7 @@ ddsrt_getrusage(int who, ddsrt_rusage_t *usage)
|
||||||
assert(usage != NULL);
|
assert(usage != NULL);
|
||||||
|
|
||||||
if (who == DDSRT_RUSAGE_THREAD) {
|
if (who == DDSRT_RUSAGE_THREAD) {
|
||||||
rc = rusage_thread(usage);
|
rc = rusage_thread_anythread(xTaskGetCurrentTaskHandle(), usage);
|
||||||
} else {
|
} else {
|
||||||
rc = rusage_self(usage);
|
rc = rusage_self(usage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,87 +15,212 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/resource.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_init.h>
|
||||||
#include <mach/mach_port.h>
|
#include <mach/mach_port.h>
|
||||||
#include <mach/thread_act.h>
|
#include <mach/thread_act.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "dds/ddsrt/rusage.h"
|
|
||||||
|
|
||||||
dds_return_t
|
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;
|
struct rusage buf;
|
||||||
dds_return_t rc;
|
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)
|
||||||
#if defined(__linux)
|
return DDS_RETCODE_ERROR;
|
||||||
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;
|
|
||||||
}
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
switch (who) {
|
||||||
|
case DDSRT_RUSAGE_THREAD:
|
||||||
|
if ((rc = ddsrt_getrusage_anythread (pthread_mach_thread_np (pthread_self()), usage)) < 0)
|
||||||
return rc;
|
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;
|
||||||
|
}
|
||||||
|
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>
|
#include <psapi.h>
|
||||||
|
|
||||||
dds_time_t
|
dds_time_t
|
||||||
filetime_to_time(const FILETIME *ft)
|
filetime_to_time (const FILETIME *ft)
|
||||||
{
|
{
|
||||||
/* FILETIME structures express times in 100-nanosecond time units. */
|
/* 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
|
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;
|
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
|
/* Memory counters are per process, but populate them if thread resource
|
||||||
usage is requested to keep in sync with Linux. */
|
usage is requested to keep in sync with Linux. */
|
||||||
if ((!GetProcessMemoryInfo(GetCurrentProcess(), &pmctrs, sizeof(pmctrs)))
|
if (!GetThreadTimes (tid, &ctime, &etime, &stime, &utime))
|
||||||
|| (who == DDSRT_RUSAGE_SELF &&
|
return DDS_RETCODE_ERROR;
|
||||||
!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);
|
||||||
|
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));
|
memset(usage, 0, sizeof(*usage));
|
||||||
usage->stime = filetime_to_time(&stime);
|
usage->stime = filetime_to_time(&stime);
|
||||||
usage->utime = filetime_to_time(&utime);
|
usage->utime = filetime_to_time(&utime);
|
||||||
usage->maxrss = pmctrs.PeakWorkingSetSize;
|
}
|
||||||
|
|
||||||
|
usage->maxrss = pmctrs.PeakWorkingSetSize;
|
||||||
return DDS_RETCODE_OK;
|
return DDS_RETCODE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "dds/ddsrt/string.h"
|
#include "dds/ddsrt/string.h"
|
||||||
#include "dds/ddsrt/threads_priv.h"
|
#include "dds/ddsrt/threads_priv.h"
|
||||||
#include "dds/ddsrt/types.h"
|
#include "dds/ddsrt/types.h"
|
||||||
|
#include "dds/ddsrt/static_assert.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -39,9 +40,14 @@ typedef struct {
|
||||||
|
|
||||||
#if defined(__linux)
|
#if defined(__linux)
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
#include <dirent.h>
|
||||||
#define MAXTHREADNAMESIZE (15) /* 16 bytes including null-terminating byte. */
|
#define MAXTHREADNAMESIZE (15) /* 16 bytes including null-terminating byte. */
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
|
#include <mach/mach_init.h>
|
||||||
#include <mach/thread_info.h> /* MAXTHREADNAMESIZE */
|
#include <mach/thread_info.h> /* MAXTHREADNAMESIZE */
|
||||||
|
#include <mach/task.h>
|
||||||
|
#include <mach/task_info.h>
|
||||||
|
#include <mach/vm_map.h>
|
||||||
#elif defined(__sun)
|
#elif defined(__sun)
|
||||||
#define MAXTHREADNAMESIZE (31)
|
#define MAXTHREADNAMESIZE (31)
|
||||||
#elif defined(__FreeBSD__)
|
#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)
|
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;
|
return DDS_RETCODE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +378,104 @@ ddsrt_thread_join(ddsrt_thread_t thread, uint32_t *thread_result)
|
||||||
return DDS_RETCODE_OK;
|
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_key_t thread_cleanup_key;
|
||||||
static pthread_once_t thread_once = PTHREAD_ONCE_INIT;
|
static pthread_once_t thread_once = PTHREAD_ONCE_INIT;
|
||||||
|
|
|
@ -16,6 +16,60 @@
|
||||||
#include "dds/ddsrt/string.h"
|
#include "dds/ddsrt/string.h"
|
||||||
#include "dds/ddsrt/threads_priv.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 {
|
typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
ddsrt_thread_routine_t routine;
|
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
|
/* 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] = "";
|
static ddsrt_thread_local char thread_name[16] = "";
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
|
@ -197,6 +254,16 @@ ddsrt_thread_getname(
|
||||||
return cnt;
|
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;
|
static const DWORD MS_VC_EXCEPTION=0x406D1388;
|
||||||
|
|
||||||
#pragma pack(push,8)
|
#pragma pack(push,8)
|
||||||
|
@ -209,19 +276,23 @@ typedef struct tagTHREADNAME_INFO
|
||||||
} THREADNAME_INFO;
|
} THREADNAME_INFO;
|
||||||
#pragma pack(pop)
|
#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
|
void
|
||||||
ddsrt_thread_setname(
|
ddsrt_thread_setname(
|
||||||
const char *__restrict name)
|
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;
|
THREADNAME_INFO info;
|
||||||
info.dwType = 0x1000;
|
info.dwType = 0x1000;
|
||||||
info.szName = name;
|
info.szName = name;
|
||||||
|
@ -241,10 +312,73 @@ ddsrt_thread_setname(
|
||||||
/* Suppress warnings. */
|
/* Suppress warnings. */
|
||||||
}
|
}
|
||||||
#pragma warning(pop)
|
#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;
|
static ddsrt_thread_local thread_cleanup_t *thread_cleanup = NULL;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue