1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
/*
* Linux DTrace
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
#!/usr/sbin/dtrace -s
/*
* NAME
* thread-ids.d - show the mapping between Pthread IDs and tid values
*
* SYNOPSIS
* sudo ./thread-ids.d -c "<name-of-app> [app options]"
*
* DESCRIPTION
* This script assumes that the target uses the Pthreads library
* to create one or more threads. It shows the mapping of the
* Pthread thread IDs and the thread ID, as returned in the tid
* built-in variable.
*
* NOTES
* - In addition to showing how to uncover this mapping, this
* script also shows a technique how to retrieve a value from
* a pointer argument in a function call.
*
* In this case, this is the thread ID that pthread_create()
* returns in its first argument.
*
* This is from the man page for pthread_create():
*
* int pthread_create(pthread_t *restrict thread,
* const pthread_attr_t *restrict attr,
* void *(*start_routine)(void *),
* void *restrict arg);
*
* We need to capture the contents of *thread.
*
* In this case, we cannot use the built-in tid variable
* within pthread_create(), because typically, this function
* is executed by one thread, the main thread. This means
* that the value in tid is the thread ID of this main thread,
* but we need to have the value of the thread that is created
* as a result of calling pthread_create(). As shown below,
* this can be done by tracing clone3(), which is called by
* pthread_create().
* - It is assumed that a function called main is executed.
* If this is not the case, this is not a critical error.
* The first probe is used to capture the tid value for the main
* program. This thread is however not created by function
* pthread_create() and therefore this part of the script is
* not essential.
* This is why this probe and corresponding printf() statement
* in the END probe can safely be removed, or replaced by a
* suitable alternative.
*/
/*
* Suppress the default output from the dtrace command and have
* printa() print the aggregation data sorted by the first field.
*/
#pragma D option quiet
#pragma D option aggsortkey=1
#pragma D option aggsortkeypos=0
/*
* Declare a thread-local variable. This is to ensure that the
* compiler sees it before it is referenced.
*/
self int clone_tid;
/*
* Store the thread ID of the main thread.
*/
pid$target:a.out:main:entry
{
tid_main = tid;
}
/*
* Variable pthr_id_p captures the first argument of the call to
* pthread_create(). This is a pointer, but we can't dereference
* it here, because the contents this pointer points to, are only
* available upon return.
* This is solved by storing the pointer here. In the return probe
* for this function, we can then dereference the pointer.
*/
pid$target:libc.so:pthread_create:entry
{
self->pthr_id_p = (int64_t *) arg0;
}
/*
* We actually know that clone3() is called. By using the wildcard
* here, the script continues to work in case this number changes in
* the future.
*/
pid$target:libc.so:clone*:return
/ self->pthr_id_p !=NULL /
{
/*
* We know that one of the clone functions is called from within
* pthread_create() and it returns the thread ID that DTrace stores
* in the tid variable.
* This is why the return value, which is stored in arg1, is copied
* into a thread-local variable called clone_tid. This variable is
* then referenced in the return probe for pthread_create().
*/
self->clone_tid = arg1;
}
/*
* This is where things come together.
*
* We already have the value for the thread ID as used by DTrace.
* It is stored in thread-local variable clone_tid.
*
* Now we can capture the Pthreads thread ID.
*
* There is one more thing to this though. Below we use an
* aggregation to store the results (and ignore the count when
* printing the results), but this is not strictly necessary.
*
* The approach chosen here allows us to control the sorting of
* the results. In this case the sort field has been set to the
* Pthreads thread ID, but this can easily be changed.
*
* If there is no need to print the data sorted, a simple printf()
* will do. All that needs to be done then is to print the two
* variables this->pthr_id and self->clone_tid.
*
*/
pid$target:libc.so:pthread_create:return
{
/*
* We are about to return from pthread_create() and can dereference
* the pointer.
* Before we do so, the data needs to be copied from user space into
* the kernel. Since this is a 64 bit address, 8 bytes are copied.
* The value is what pthread_create() returns in its *thread first
* argument, which is the Pthreads thread ID.
* This gives us both thread IDs and they are used in the key for the
* aggregation called thread_mapping.
*/
this->pthr_id = *(int64_t *) copyin(*self->pthr_id_p,8);
@thread_mapping[this->pthr_id,self->clone_tid] = count();
/*
* Free the storage for the thread-local variables.
*/
self->pthr_id_p = 0;
self->clone_tid = 0;
}
/*
* The aggregation is printed in the END probe. We use printf()
* statements to print the thread ID of the main program and the
* table header.
* Note that there is no format field for the value for the
* aggregation. As explained above, the value is not relevant
* in this case.
*
* Note that we do not need to include these print statements,
* because aggregations that are not explictly printed, are
* automatically printed when the script terminates. The reason
* we print them ourselves is to have control over the lay-out.
*/
END
{
printf("Thread ID of main program: %d\n\n",tid_main);
printf("%16s <=> %-9s\n\n","Pthreads ID","Thread ID");
printa("%16d <=> %-9d\n",@thread_mapping);
}
|