app, tools: add "running" thread attribute to GimpBacktrace/performance-log
The "running" attribute (readable through
gimp_backtrace_is_thread_running(), and recorded in the performance
log) specifies if the thread was in a running or suspended state at
the time the backtrace was taken. It is accurate on Linux, but
only approximated on Windows.
Adapt the performance-log-expand.py tool to maintain this attribute
(and any future thread attributes we might add).
(cherry picked from commit 78adb7c900
)
This commit is contained in:
@ -42,6 +42,7 @@
|
||||
#include <execinfo.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_LIBUNWIND
|
||||
#define UNW_LOCAL_ONLY
|
||||
@ -68,6 +69,7 @@ struct _GimpBacktraceThread
|
||||
{
|
||||
pid_t tid;
|
||||
gchar name[MAX_THREAD_NAME_SIZE];
|
||||
gchar state;
|
||||
|
||||
guintptr frames[MAX_N_FRAMES];
|
||||
gint n_frames;
|
||||
@ -93,6 +95,7 @@ static gint gimp_backtrace_enumerate_threads (gboolean include_cu
|
||||
static void gimp_backtrace_read_thread_name (pid_t tid,
|
||||
gchar *name,
|
||||
gint size);
|
||||
static gchar gimp_backtrace_read_thread_state (pid_t tid);
|
||||
|
||||
static void gimp_backtrace_signal_handler (gint signum);
|
||||
|
||||
@ -210,6 +213,34 @@ gimp_backtrace_read_thread_name (pid_t tid,
|
||||
}
|
||||
}
|
||||
|
||||
static gchar
|
||||
gimp_backtrace_read_thread_state (pid_t tid)
|
||||
{
|
||||
gchar buffer[64];
|
||||
gint fd;
|
||||
gchar state = '\0';
|
||||
|
||||
g_snprintf (buffer, sizeof (buffer),
|
||||
"/proc/self/task/%llu/stat",
|
||||
(unsigned long long) tid);
|
||||
|
||||
fd = open (buffer, O_RDONLY);
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
gint n = read (fd, buffer, sizeof (buffer));
|
||||
|
||||
if (n > 0)
|
||||
buffer[n - 1] = '\0';
|
||||
|
||||
sscanf (buffer, "%*d %*s %c", &state);
|
||||
|
||||
close (fd);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_backtrace_signal_handler (gint signum)
|
||||
{
|
||||
@ -367,6 +398,8 @@ gimp_backtrace_new (gboolean include_current_thread)
|
||||
gimp_backtrace_read_thread_name (thread->tid,
|
||||
thread->name, MAX_THREAD_NAME_SIZE);
|
||||
|
||||
thread->state = gimp_backtrace_read_thread_state (thread->tid);
|
||||
|
||||
syscall (SYS_tgkill, pid, threads[i], BACKTRACE_SIGNAL);
|
||||
}
|
||||
|
||||
@ -460,7 +493,7 @@ const gchar *
|
||||
gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
|
||||
gint thread)
|
||||
{
|
||||
g_return_val_if_fail (backtrace != NULL, 0);
|
||||
g_return_val_if_fail (backtrace != NULL, NULL);
|
||||
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL);
|
||||
|
||||
if (backtrace->threads[thread].name[0])
|
||||
@ -469,6 +502,16 @@ gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gimp_backtrace_is_thread_running (GimpBacktrace *backtrace,
|
||||
gint thread)
|
||||
{
|
||||
g_return_val_if_fail (backtrace != NULL, FALSE);
|
||||
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE);
|
||||
|
||||
return backtrace->threads[thread].state == 'R';
|
||||
}
|
||||
|
||||
gint
|
||||
gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
|
||||
guintptr thread_id,
|
||||
|
@ -85,6 +85,13 @@ gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gimp_backtrace_is_thread_running (GimpBacktrace *backtrace,
|
||||
gint thread)
|
||||
{
|
||||
g_return_val_if_reached (FALSE);
|
||||
}
|
||||
|
||||
gint
|
||||
gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
|
||||
guintptr thread_id,
|
||||
|
@ -55,13 +55,20 @@ typedef struct _GimpBacktraceThread GimpBacktraceThread;
|
||||
struct _Thread
|
||||
{
|
||||
DWORD tid;
|
||||
gchar *name;
|
||||
|
||||
union
|
||||
{
|
||||
gchar *name;
|
||||
guint64 time;
|
||||
};
|
||||
};
|
||||
|
||||
struct _GimpBacktraceThread
|
||||
{
|
||||
DWORD tid;
|
||||
const gchar *name;
|
||||
guint64 time;
|
||||
guint64 last_time;
|
||||
|
||||
guintptr frames[MAX_N_FRAMES];
|
||||
gint n_frames;
|
||||
@ -96,6 +103,8 @@ gint64 last_thread_enumeration_time;
|
||||
Thread thread_names[MAX_N_THREADS];
|
||||
gint n_thread_names;
|
||||
gint thread_names_spinlock;
|
||||
Thread thread_times[MAX_N_THREADS];
|
||||
gint n_thread_times;
|
||||
|
||||
DWORD WINAPI (* gimp_backtrace_SymSetOptions) (DWORD SymOptions);
|
||||
BOOL WINAPI (* gimp_backtrace_SymInitialize) (HANDLE hProcess,
|
||||
@ -308,6 +317,7 @@ gimp_backtrace_start (void)
|
||||
{
|
||||
n_threads = 0;
|
||||
last_thread_enumeration_time = 0;
|
||||
n_thread_times = 0;
|
||||
|
||||
initialized = TRUE;
|
||||
}
|
||||
@ -374,6 +384,10 @@ gimp_backtrace_new (gboolean include_current_thread)
|
||||
CONTEXT context = {};
|
||||
STACKFRAME64 frame = {};
|
||||
DWORD machine_type;
|
||||
FILETIME creation_time;
|
||||
FILETIME exit_time;
|
||||
FILETIME kernel_time;
|
||||
FILETIME user_time;
|
||||
|
||||
if (! include_current_thread && threads[i].tid == tid)
|
||||
continue;
|
||||
@ -426,8 +440,11 @@ gimp_backtrace_new (gboolean include_current_thread)
|
||||
#error unsupported architecture
|
||||
#endif
|
||||
|
||||
thread->tid = threads[i].tid;
|
||||
thread->name = threads[i].name;
|
||||
thread->tid = threads[i].tid;
|
||||
thread->name = threads[i].name;
|
||||
thread->last_time = 0;
|
||||
thread->time = 0;
|
||||
|
||||
thread->n_frames = 0;
|
||||
|
||||
while (thread->n_frames < MAX_N_FRAMES &&
|
||||
@ -443,6 +460,35 @@ gimp_backtrace_new (gboolean include_current_thread)
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetThreadTimes (hThread,
|
||||
&creation_time, &exit_time,
|
||||
&kernel_time, &user_time))
|
||||
{
|
||||
thread->time = (((guint64) kernel_time.dwHighDateTime << 32) |
|
||||
((guint64) kernel_time.dwLowDateTime)) +
|
||||
(((guint64) user_time.dwHighDateTime << 32) |
|
||||
((guint64) user_time.dwLowDateTime));
|
||||
|
||||
if (i < n_thread_times && thread->tid == thread_times[i].tid)
|
||||
{
|
||||
thread->last_time = thread_times[i].time;
|
||||
}
|
||||
else
|
||||
{
|
||||
gint j;
|
||||
|
||||
for (j = 0; j < n_thread_times; j++)
|
||||
{
|
||||
if (thread->tid == thread_times[j].tid)
|
||||
{
|
||||
thread->last_time = thread_times[j].time;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (threads[i].tid != tid)
|
||||
ResumeThread (hThread);
|
||||
|
||||
@ -452,6 +498,14 @@ gimp_backtrace_new (gboolean include_current_thread)
|
||||
backtrace->n_threads++;
|
||||
}
|
||||
|
||||
n_thread_times = backtrace->n_threads;
|
||||
|
||||
for (i = 0; i < backtrace->n_threads; i++)
|
||||
{
|
||||
thread_times[i].tid = backtrace->threads[i].tid;
|
||||
thread_times[i].time = backtrace->threads[i].time;
|
||||
}
|
||||
|
||||
g_mutex_unlock (&mutex);
|
||||
|
||||
if (backtrace->n_threads == 0)
|
||||
@ -503,6 +557,17 @@ gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
|
||||
return backtrace->threads[thread].name;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gimp_backtrace_is_thread_running (GimpBacktrace *backtrace,
|
||||
gint thread)
|
||||
{
|
||||
g_return_val_if_fail (backtrace != NULL, FALSE);
|
||||
g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE);
|
||||
|
||||
return backtrace->threads[thread].time >
|
||||
backtrace->threads[thread].last_time;
|
||||
}
|
||||
|
||||
gint
|
||||
gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
|
||||
guintptr thread_id,
|
||||
|
@ -50,6 +50,8 @@ guintptr gimp_backtrace_get_thread_id (GimpBacktrace *backt
|
||||
gint thread);
|
||||
const gchar * gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
|
||||
gint thread);
|
||||
gboolean gimp_backtrace_is_thread_running (GimpBacktrace *backtrace,
|
||||
gint thread);
|
||||
|
||||
gint gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
|
||||
guintptr thread_id,
|
||||
|
@ -3620,9 +3620,11 @@ gimp_dashboard_log_sample (GimpDashboard *dashboard,
|
||||
{
|
||||
guintptr thread_id;
|
||||
const gchar *thread_name;
|
||||
gint last_running = -1;
|
||||
gint running;
|
||||
gint n_frames;
|
||||
gint n_head = 0;
|
||||
gint n_tail = 0;
|
||||
gint n_head = 0;
|
||||
gint n_tail = 0;
|
||||
gint frame;
|
||||
|
||||
thread_id = gimp_backtrace_get_thread_id (backtrace, thread);
|
||||
@ -3640,6 +3642,9 @@ gimp_dashboard_log_sample (GimpDashboard *dashboard,
|
||||
gint n;
|
||||
gint i;
|
||||
|
||||
last_running = gimp_backtrace_is_thread_running (
|
||||
priv->log_backtrace, other_thread);
|
||||
|
||||
n = gimp_backtrace_get_n_frames (priv->log_backtrace,
|
||||
other_thread);
|
||||
n = MIN (n, n_frames);
|
||||
@ -3673,7 +3678,9 @@ gimp_dashboard_log_sample (GimpDashboard *dashboard,
|
||||
}
|
||||
}
|
||||
|
||||
if (n_head + n_tail == n_frames)
|
||||
running = gimp_backtrace_is_thread_running (backtrace, thread);
|
||||
|
||||
if (running == last_running && n_head + n_tail == n_frames)
|
||||
continue;
|
||||
|
||||
BACKTRACE_NONEMPTY ();
|
||||
@ -3691,6 +3698,10 @@ gimp_dashboard_log_sample (GimpDashboard *dashboard,
|
||||
"\"");
|
||||
}
|
||||
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
" running=\"%d\"",
|
||||
running);
|
||||
|
||||
if (n_head > 0)
|
||||
{
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
@ -3705,25 +3716,33 @@ gimp_dashboard_log_sample (GimpDashboard *dashboard,
|
||||
n_tail);
|
||||
}
|
||||
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
">\n");
|
||||
|
||||
for (frame = n_head; frame < n_frames - n_tail; frame++)
|
||||
if (n_head + n_tail < n_frames)
|
||||
{
|
||||
unsigned long long address;
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
">\n");
|
||||
|
||||
address = gimp_backtrace_get_frame_address (backtrace,
|
||||
thread, frame);
|
||||
for (frame = n_head; frame < n_frames - n_tail; frame++)
|
||||
{
|
||||
unsigned long long address;
|
||||
|
||||
address = gimp_backtrace_get_frame_address (backtrace,
|
||||
thread, frame);
|
||||
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
"<frame address=\"0x%llx\" />\n",
|
||||
address);
|
||||
|
||||
g_hash_table_add (priv->log_addresses, (gpointer) address);
|
||||
}
|
||||
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
"<frame address=\"0x%llx\" />\n",
|
||||
address);
|
||||
|
||||
g_hash_table_add (priv->log_addresses, (gpointer) address);
|
||||
"</thread>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
" />\n");
|
||||
}
|
||||
|
||||
gimp_dashboard_log_printf (dashboard,
|
||||
"</thread>\n");
|
||||
}
|
||||
|
||||
if (! backtrace_empty)
|
||||
|
@ -63,37 +63,38 @@ for sample in (log.find ("samples") or empty_element).iterfind ("sample"):
|
||||
|
||||
for thread in backtrace:
|
||||
id = thread.get ("id")
|
||||
head = thread.get ("head")
|
||||
tail = thread.get ("tail")
|
||||
attrib = dict (thread.attrib)
|
||||
frames = list (thread)
|
||||
|
||||
last_thread = last_backtrace.setdefault (id, [{}, []])
|
||||
last_frames = last_thread[1]
|
||||
|
||||
if head:
|
||||
frames = last_frames[:int (head)] + frames
|
||||
|
||||
del attrib["head"]
|
||||
|
||||
if tail:
|
||||
frames = frames + last_frames[-int (tail):]
|
||||
|
||||
del attrib["tail"]
|
||||
|
||||
last_thread[0] = attrib
|
||||
last_thread[1] = frames
|
||||
|
||||
if not frames:
|
||||
last_backtrace.pop (id, None)
|
||||
else:
|
||||
last_thread = last_backtrace.setdefault (id, [None, []])
|
||||
last_frames = last_thread[1]
|
||||
|
||||
name = thread.get ("name")
|
||||
head = thread.get ("head")
|
||||
tail = thread.get ("tail")
|
||||
|
||||
if head:
|
||||
frames = last_frames[:int (head)] + frames
|
||||
if tail:
|
||||
frames = frames + last_frames[-int (tail):]
|
||||
|
||||
last_thread[0] = name
|
||||
last_thread[1] = frames
|
||||
del last_backtrace[id]
|
||||
|
||||
for thread in list (backtrace):
|
||||
backtrace.remove (thread)
|
||||
|
||||
for id, (name, frames) in last_backtrace.items ():
|
||||
thread = ElementTree.SubElement (backtrace, "thread", id=id)
|
||||
for id, (attrib, frames) in last_backtrace.items ():
|
||||
thread = ElementTree.SubElement (backtrace, "thread", attrib)
|
||||
thread.text = "\n"
|
||||
thread.tail = "\n"
|
||||
|
||||
if name:
|
||||
thread.set ("name", name)
|
||||
|
||||
thread.extend (frames)
|
||||
|
||||
# Expand address map
|
||||
|
Reference in New Issue
Block a user