[gimp/gimp-2-10] app, tools: add "running" thread attribute to GimpBacktrace/performance-log
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-2-10] app, tools: add "running" thread attribute to GimpBacktrace/performance-log
- Date: Mon, 3 Sep 2018 22:30:53 +0000 (UTC)
commit 0fcf02a7c6bdda3cae1d0e57d614d9a42a6d4e33
Author: Ell <ell_se yahoo com>
Date: Mon Sep 3 18:13:16 2018 -0400
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 78adb7c9008011161009153a56354198c0842661)
app/core/gimpbacktrace-linux.c | 45 ++++++++++++++++++++++++-
app/core/gimpbacktrace-none.c | 7 ++++
app/core/gimpbacktrace-windows.c | 71 ++++++++++++++++++++++++++++++++++++++--
app/core/gimpbacktrace.h | 2 ++
app/widgets/gimpdashboard.c | 53 ++++++++++++++++++++----------
tools/performance-log-expand.py | 39 +++++++++++-----------
6 files changed, 177 insertions(+), 40 deletions(-)
---
diff --git a/app/core/gimpbacktrace-linux.c b/app/core/gimpbacktrace-linux.c
index a76b5be2ce..43859df1ab 100644
--- a/app/core/gimpbacktrace-linux.c
+++ b/app/core/gimpbacktrace-linux.c
@@ -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,
diff --git a/app/core/gimpbacktrace-none.c b/app/core/gimpbacktrace-none.c
index 489a63c7fe..087e8cb518 100644
--- a/app/core/gimpbacktrace-none.c
+++ b/app/core/gimpbacktrace-none.c
@@ -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,
diff --git a/app/core/gimpbacktrace-windows.c b/app/core/gimpbacktrace-windows.c
index 2f60f51524..8fe705e818 100644
--- a/app/core/gimpbacktrace-windows.c
+++ b/app/core/gimpbacktrace-windows.c
@@ -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,
diff --git a/app/core/gimpbacktrace.h b/app/core/gimpbacktrace.h
index a4ea2c2a30..8c172b2d13 100644
--- a/app/core/gimpbacktrace.h
+++ b/app/core/gimpbacktrace.h
@@ -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,
diff --git a/app/widgets/gimpdashboard.c b/app/widgets/gimpdashboard.c
index a53d91ebfe..9f25e83934 100644
--- a/app/widgets/gimpdashboard.c
+++ b/app/widgets/gimpdashboard.c
@@ -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;
- gimp_dashboard_log_printf (dashboard,
- "<frame address=\"0x%llx\" />\n",
- address);
+ address = gimp_backtrace_get_frame_address (backtrace,
+ thread, frame);
- g_hash_table_add (priv->log_addresses, (gpointer) address);
- }
+ gimp_dashboard_log_printf (dashboard,
+ "<frame address=\"0x%llx\" />\n",
+ address);
- gimp_dashboard_log_printf (dashboard,
- "</thread>\n");
+ g_hash_table_add (priv->log_addresses, (gpointer) address);
+ }
+
+ gimp_dashboard_log_printf (dashboard,
+ "</thread>\n");
+ }
+ else
+ {
+ gimp_dashboard_log_printf (dashboard,
+ " />\n");
+ }
}
if (! backtrace_empty)
diff --git a/tools/performance-log-expand.py b/tools/performance-log-expand.py
index c0f7cc9471..36fc1b6c8e 100755
--- a/tools/performance-log-expand.py
+++ b/tools/performance-log-expand.py
@@ -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)
- if not frames:
- last_backtrace.pop (id, None)
- else:
- last_thread = last_backtrace.setdefault (id, [None, []])
- last_frames = last_thread[1]
+ last_thread = last_backtrace.setdefault (id, [{}, []])
+ last_frames = last_thread[1]
+
+ if head:
+ frames = last_frames[:int (head)] + frames
+
+ del attrib["head"]
- name = thread.get ("name")
- head = thread.get ("head")
- tail = thread.get ("tail")
+ if tail:
+ frames = frames + last_frames[-int (tail):]
- if head:
- frames = last_frames[:int (head)] + frames
- if tail:
- frames = frames + last_frames[-int (tail):]
+ del attrib["tail"]
- last_thread[0] = name
- last_thread[1] = frames
+ last_thread[0] = attrib
+ last_thread[1] = frames
+
+ if not 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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]