1 /*
2 * thread.c
3 *
4 * Copyright 2012 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #define _WIN32_WINNT 0x0501 /* for DebugBreakProcess(), must be before any #include-s */
25
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30
31 #include "common.h"
32
33 #ifdef G_OS_UNIX
34 #include <signal.h>
35 #else
36 #include <limits.h>
37 #include <windows.h>
38 #endif
39
40 enum
41 {
42 GROUP_ID,
43 GROUP_PID
44 };
45
46 static ScpTreeStore *groups;
47
on_thread_group_started(GArray * nodes)48 void on_thread_group_started(GArray *nodes)
49 {
50 const char *gid = parse_lead_value(nodes);
51 const char *pid = parse_find_value(nodes, "pid");
52
53 ui_set_statusbar(TRUE, _("Thread group %s started."), pid ? pid : gid);
54
55 iff (pid, "no pid")
56 {
57 GtkTreeIter iter;
58
59 iff (store_find(groups, &iter, GROUP_ID, gid), "%s: gid not found", gid)
60 scp_tree_store_set(groups, &iter, GROUP_PID, pid, -1);
61 }
62 }
63
on_thread_group_exited(GArray * nodes)64 void on_thread_group_exited(GArray *nodes)
65 {
66 const char *gid = parse_lead_value(nodes);
67 const char *exit_code = parse_find_value(nodes, "exit-code");
68 GString *status = g_string_new(_("Thread group "));
69 GtkTreeIter iter;
70
71 if (store_find(groups, &iter, GROUP_ID, gid))
72 {
73 const char *pid;
74
75 scp_tree_store_get(groups, &iter, GROUP_PID, &pid, -1);
76 if (pid)
77 {
78 g_string_append(status, pid);
79 scp_tree_store_set(groups, &iter, GROUP_PID, NULL, -1);
80 }
81 }
82 else
83 {
84 dc_error("%s: gid not found", gid);
85 g_string_append(status, gid);
86 }
87
88 g_string_append(status, _(" exited"));
89 if (exit_code)
90 {
91 g_string_append_printf(status, _(" with exit code %s"), exit_code);
92 #ifdef G_OS_UNIX
93 if (terminal_show_on_error)
94 terminal_standalone(TRUE);
95 #endif
96 }
97 ui_set_statusbar(TRUE, _("%s."), status->str);
98 g_string_free(status, TRUE);
99 }
100
on_thread_group_added(GArray * nodes)101 void on_thread_group_added(GArray *nodes)
102 {
103 GtkTreeIter iter;
104 scp_tree_store_append(groups, &iter, NULL);
105 scp_tree_store_set(groups, &iter, GROUP_ID, parse_lead_value(nodes), -1);
106 }
107
on_thread_group_removed(GArray * nodes)108 void on_thread_group_removed(GArray *nodes)
109 {
110 const char *gid = parse_lead_value(nodes);
111 GtkTreeIter iter;
112
113 iff (store_find(groups, &iter, GROUP_ID, gid), "%s: gid not found", gid)
114 scp_tree_store_remove(groups, &iter);
115 }
116
117 enum
118 {
119 THREAD_ID,
120 THREAD_FILE,
121 THREAD_LINE,
122 THREAD_PID,
123 THREAD_GROUP_ID,
124 THREAD_STATE,
125 THREAD_BASE_NAME,
126 THREAD_FUNC,
127 THREAD_ADDR,
128 THREAD_TARGET_ID,
129 THREAD_CORE
130 };
131
thread_ident_compare(ScpTreeStore * store,GtkTreeIter * a,GtkTreeIter * b,gpointer gdata)132 static gint thread_ident_compare(ScpTreeStore *store, GtkTreeIter *a, GtkTreeIter *b,
133 gpointer gdata)
134 {
135 const char *s1, *s2;
136
137 scp_tree_store_get(store, a, GPOINTER_TO_INT(gdata), &s1, -1);
138 scp_tree_store_get(store, b, GPOINTER_TO_INT(gdata), &s2, -1);
139
140 if (s1 && s2)
141 {
142 const char *p1, *p2;
143
144 for (p1 = s1; *p1 && !isdigit(*p1); p1++);
145 for (p2 = s2; *p2 && !isdigit(*p2); p2++);
146
147 if (p1 - s1 == p2 - s2 && !memcmp(s1, s2, p1 - s1))
148 return atoi(p1) - atoi(p2);
149 }
150
151 return g_strcmp0(s1, s2);
152 }
153
154 static ScpTreeStore *store;
155 static GtkTreeSelection *selection;
156
find_thread(const char * tid,GtkTreeIter * iter)157 static gboolean find_thread(const char *tid, GtkTreeIter *iter)
158 {
159 if (G_LIKELY(store_find(store, iter, THREAD_ID, tid)))
160 return TRUE;
161
162 dc_error("%s: tid not found", tid);
163 return FALSE;
164 }
165
166 static const gchar *RUNNING;
167 static const gchar *STOPPED;
168
auto_select_thread(void)169 static void auto_select_thread(void)
170 {
171 GtkTreeIter iter;
172
173 if (store_find(store, &iter, THREAD_STATE, STOPPED))
174 {
175 utils_tree_set_cursor(selection, &iter, -1);
176 view_seek_selected(selection, FALSE, SK_EXECUTE);
177 }
178 }
179
180 guint thread_count = 0;
181 const char *thread_id = NULL;
182 ThreadState thread_state = THREAD_BLANK;
183 guint thread_prompt = 0;
184
thread_group_id(void)185 const char *thread_group_id(void)
186 {
187 GtkTreeIter iter;
188 const char *gid = NULL;
189
190 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
191 scp_tree_store_get(store, &iter, THREAD_GROUP_ID, &gid, -1);
192
193 return gid;
194 }
195
thread_iter_unmark(GtkTreeIter * iter,gpointer gdata)196 static void thread_iter_unmark(GtkTreeIter *iter, gpointer gdata)
197 {
198 const char *file;
199 const gchar *state;
200 gint line;
201 gboolean stopped;
202
203 scp_tree_store_get(store, iter, THREAD_FILE, &file, THREAD_LINE, &line, THREAD_STATE,
204 &state, -1);
205 stopped = !strcmp(state, STOPPED);
206 thread_prompt += gdata ? -stopped : !stopped;
207
208 if (GPOINTER_TO_INT(gdata) != 2)
209 utils_mark(file, line, FALSE, MARKER_EXECUTE);
210 }
211
thread_iter_running(GtkTreeIter * iter,const char * tid)212 static void thread_iter_running(GtkTreeIter *iter, const char *tid)
213 {
214 thread_iter_unmark(iter, GINT_TO_POINTER(TRUE + pref_keep_exec_point));
215
216 scp_tree_store_set(store, iter, THREAD_STATE, RUNNING, pref_keep_exec_point ? -1 :
217 THREAD_FILE, NULL, THREAD_LINE, 0, THREAD_BASE_NAME, NULL, THREAD_FUNC, NULL,
218 THREAD_ADDR, NULL, THREAD_CORE, NULL, -1);
219
220 if (thread_id)
221 {
222 if (!tid)
223 scp_tree_store_get(store, iter, THREAD_ID, &tid, -1);
224
225 if (!strcmp(tid, thread_id))
226 thread_state = THREAD_RUNNING;
227 }
228 }
229
230 gboolean thread_select_on_running;
231 gboolean thread_select_on_stopped;
232 gboolean thread_select_on_exited;
233 gboolean thread_select_follow;
234
on_thread_running(GArray * nodes)235 void on_thread_running(GArray *nodes)
236 {
237 const char *tid = parse_find_value(nodes, "thread-id");
238
239 iff (tid, "no tid")
240 {
241 gboolean was_stopped = thread_state >= THREAD_STOPPED;
242
243 if (!strcmp(tid, "all"))
244 store_foreach(store, (GFunc) thread_iter_running, NULL);
245 else
246 {
247 GtkTreeIter iter;
248
249 if (find_thread(tid, &iter))
250 thread_iter_running(&iter, tid);
251 }
252
253 if (thread_select_on_running && was_stopped && thread_state == THREAD_RUNNING)
254 auto_select_thread();
255 }
256 }
257
thread_parse_extra(GArray * nodes,GtkTreeIter * iter,const char * name,int column)258 static void thread_parse_extra(GArray *nodes, GtkTreeIter *iter, const char *name, int column)
259 {
260 const char *value = parse_find_value(nodes, name);
261
262 if (value)
263 scp_tree_store_set(store, iter, column, value, -1);
264 }
265
thread_parse_frame(GArray * frame,const char * tid,GtkTreeIter * iter)266 static void thread_parse_frame(GArray *frame, const char *tid, GtkTreeIter *iter)
267 {
268 ParseLocation loc;
269
270 parse_location(frame, &loc);
271 if (!loc.addr)
272 loc.addr = "??";
273
274 thread_iter_unmark(iter, NULL);
275 scp_tree_store_set(store, iter, THREAD_FILE, loc.file, THREAD_LINE, loc.line,
276 THREAD_STATE, STOPPED, THREAD_BASE_NAME, loc.base_name, THREAD_FUNC, loc.func,
277 THREAD_ADDR, loc.addr, -1);
278
279 if (!g_strcmp0(tid, thread_id))
280 {
281 if (loc.line)
282 {
283 thread_state = THREAD_AT_SOURCE;
284 utils_seek(loc.file, loc.line, FALSE, SK_EXEC_MARK);
285 }
286 else
287 {
288 thread_state = THREAD_AT_ASSEMBLER;
289 view_dirty(VIEW_CONSOLE);
290 }
291 }
292 else
293 utils_mark(loc.file, loc.line, TRUE, MARKER_EXECUTE);
294
295 parse_location_free(&loc);
296 }
297
298 typedef struct _StopData
299 {
300 const char *tid;
301 GtkTreeIter iter;
302 gboolean found;
303 } StopData;
304
thread_iter_stopped(GtkTreeIter * iter,StopData * sd)305 static void thread_iter_stopped(GtkTreeIter *iter, StopData *sd)
306 {
307 const char *tid = sd->tid, *addr;
308 const gchar *state;
309
310 scp_tree_store_get(store, iter, THREAD_STATE, &state, THREAD_ADDR, &addr,
311 tid ? -1 : THREAD_ID, &tid, -1);
312
313 if (strcmp(state, STOPPED))
314 thread_prompt++;
315 scp_tree_store_set(store, iter, THREAD_STATE, STOPPED, -1);
316
317 if (!g_strcmp0(tid, thread_id))
318 {
319 if (!addr)
320 thread_state = THREAD_QUERY_FRAME;
321
322 views_data_dirty(DS_BUSY);
323 }
324 else if (!addr)
325 view_dirty(VIEW_THREADS);
326
327 if (!sd->found)
328 {
329 sd->iter = *iter;
330 sd->found = TRUE;
331 }
332 }
333
thread_node_stopped(const ParseNode * node,StopData * sd)334 static void thread_node_stopped(const ParseNode *node, StopData *sd)
335 {
336 iff (node->type == PT_VALUE, "stopped-threads: contains array")
337 {
338 GtkTreeIter iter;
339
340 sd->tid = (const char *) node->value;
341
342 if (find_thread(sd->tid, &iter))
343 thread_iter_stopped(&iter, sd);
344 }
345 }
346
on_thread_stopped(GArray * nodes)347 void on_thread_stopped(GArray *nodes)
348 {
349 extern gint break_async;
350 const char *tid = parse_find_value(nodes, "thread-id");
351 const ParseNode *stopped = parse_find_node(nodes, "stopped-threads");
352 StopData sd;
353
354 if (tid)
355 {
356 sd.found = find_thread(tid, &sd.iter);
357
358 if (sd.found)
359 {
360 GArray *frame = parse_find_array(nodes, "frame");
361
362 if (frame)
363 thread_parse_frame(frame, tid, &sd.iter);
364
365 thread_parse_extra(nodes, &sd.iter, "core", THREAD_CORE);
366 }
367 }
368 else
369 {
370 dc_error("no tid");
371 sd.found = FALSE;
372 }
373
374 iff (stopped, "no stopped")
375 {
376 sd.tid = NULL;
377
378 if (stopped->type == PT_VALUE)
379 {
380 const char *tid = (const char *) stopped->value;
381
382 if (!strcmp(tid, "all"))
383 store_foreach(store, (GFunc) thread_iter_stopped, &sd);
384 else
385 {
386 GtkTreeIter iter;
387
388 if (find_thread(tid, &iter))
389 {
390 sd.tid = tid;
391 thread_iter_stopped(&iter, &sd);
392 }
393 }
394 }
395 else
396 parse_foreach((GArray *) stopped->value, (GFunc) thread_node_stopped, &sd);
397 }
398
399 if (thread_select_on_stopped && thread_state <= THREAD_RUNNING && sd.found)
400 {
401 utils_tree_set_cursor(selection, &sd.iter, -1);
402 view_seek_selected(selection, FALSE, SK_EXECUTE);
403 }
404
405 if (!g_strcmp0(parse_find_value(nodes, "reason"), "signal-received"))
406 plugin_blink();
407
408 if (break_async < TRUE)
409 view_dirty(VIEW_BREAKS);
410 }
411
412 static char *gdb_thread = NULL;
413
set_gdb_thread(const char * tid,gboolean select)414 static void set_gdb_thread(const char *tid, gboolean select)
415 {
416 g_free(gdb_thread);
417 gdb_thread = g_strdup(tid);
418
419 if (select)
420 {
421 GtkTreeIter iter;
422
423 if (find_thread(gdb_thread, &iter))
424 utils_tree_set_cursor(selection, &iter, -1);
425 }
426 }
427
on_thread_created(GArray * nodes)428 void on_thread_created(GArray *nodes)
429 {
430 const char *tid = parse_find_value(nodes, "id");
431 const char *gid = parse_find_value(nodes, "group-id");
432
433 if (!thread_count++)
434 {
435 /* startup */
436 breaks_reset();
437 registers_show(TRUE);
438 #ifdef G_OS_UNIX
439 terminal_clear();
440 if (terminal_auto_show)
441 terminal_standalone(TRUE);
442 #endif
443 if (option_open_panel_on_start)
444 open_debug_panel();
445 }
446
447 iff (tid, "no tid")
448 {
449 GtkTreeIter iter;
450 const char *pid = NULL;
451
452 iff (gid, "no gid")
453 iff (store_find(groups, &iter, GROUP_ID, gid), "%s: gid not found", gid)
454 scp_tree_store_get(groups, &iter, GROUP_PID, &pid, -1);
455
456 scp_tree_store_append_with_values(store, &iter, NULL, THREAD_ID, tid, THREAD_STATE,
457 "", THREAD_GROUP_ID, gid, THREAD_PID, pid, -1);
458 debug_send_format(N, "04-thread-info %s", tid);
459
460 if (thread_count == 1)
461 set_gdb_thread(tid, TRUE);
462 }
463 }
464
on_thread_exited(GArray * nodes)465 void on_thread_exited(GArray *nodes)
466 {
467 const char *tid = parse_find_value(nodes, "id");
468
469 iff (tid, "no tid")
470 {
471 GtkTreeIter iter;
472
473 if (!g_strcmp0(tid, gdb_thread))
474 set_gdb_thread(NULL, FALSE);
475
476 if (find_thread(tid, &iter))
477 {
478 gboolean was_selected = !g_strcmp0(tid, thread_id);
479
480 thread_iter_unmark(&iter, GINT_TO_POINTER(TRUE));
481 scp_tree_store_remove(store, &iter);
482 if (was_selected && thread_select_on_exited)
483 auto_select_thread();
484 }
485 }
486
487 iff (thread_count, "extra exit")
488 {
489 if (!--thread_count)
490 {
491 /* shutdown */
492 registers_show(FALSE);
493 #ifdef G_OS_UNIX
494 if (terminal_auto_hide)
495 terminal_standalone(FALSE);
496 #endif
497 on_debug_auto_exit();
498 }
499 }
500 }
501
on_thread_selected(GArray * nodes)502 void on_thread_selected(GArray *nodes)
503 {
504 set_gdb_thread(parse_lead_value(nodes), thread_select_follow);
505 }
506
thread_parse(GArray * nodes,const char * tid,gboolean stopped)507 static void thread_parse(GArray *nodes, const char *tid, gboolean stopped)
508 {
509 GtkTreeIter iter;
510
511 if (find_thread(tid, &iter))
512 {
513 if (stopped)
514 {
515 GArray *frame = parse_find_array(nodes, "frame");
516
517 iff (frame, "no frame")
518 thread_parse_frame(frame, tid, &iter);
519 }
520 else
521 {
522 const gchar *state;
523
524 scp_tree_store_get(store, &iter, THREAD_STATE, &state, -1);
525 if (strcmp(state, RUNNING))
526 thread_iter_running(&iter, tid);
527 }
528
529 thread_parse_extra(nodes, &iter, "target-id", THREAD_TARGET_ID);
530 thread_parse_extra(nodes, &iter, "core", THREAD_CORE);
531 }
532 }
533
thread_node_parse(const ParseNode * node,G_GNUC_UNUSED gpointer gdata)534 static void thread_node_parse(const ParseNode *node, G_GNUC_UNUSED gpointer gdata)
535 {
536 iff (node->type == PT_ARRAY, "threads: contains value")
537 {
538 GArray *nodes = (GArray *) node->value;
539 const char *tid = parse_find_value(nodes, "id");
540 const char *state = parse_find_value(nodes, "state");
541
542 iff (tid && state, "no tid or state")
543 thread_parse(nodes, tid, strcmp(state, "running"));
544 }
545 }
546
thread_info_parse(GArray * nodes,gboolean select)547 static const char *thread_info_parse(GArray *nodes, gboolean select)
548 {
549 const char *tid = parse_find_value(nodes, "current-thread-id");
550
551 parse_foreach(parse_lead_array(nodes), (GFunc) thread_node_parse, NULL);
552
553 if (tid)
554 set_gdb_thread(tid, select);
555
556 return tid;
557 }
558
on_thread_info(GArray * nodes)559 void on_thread_info(GArray *nodes)
560 {
561 thread_info_parse(nodes, thread_select_follow);
562 }
563
on_thread_follow(GArray * nodes)564 void on_thread_follow(GArray *nodes)
565 {
566 if (!thread_info_parse(nodes, TRUE))
567 dc_error("no current tid");
568 }
569
on_thread_frame(GArray * nodes)570 void on_thread_frame(GArray *nodes)
571 {
572 thread_parse(nodes, parse_grab_token(nodes), TRUE);
573 }
574
thread_iter_mark(GtkTreeIter * iter,GeanyDocument * doc)575 static void thread_iter_mark(GtkTreeIter *iter, GeanyDocument *doc)
576 {
577 const char *file;
578 gint line;
579
580 scp_tree_store_get(store, iter, THREAD_FILE, &file, THREAD_LINE, &line, -1);
581
582 if (line && !utils_filenamecmp(file, doc->real_path))
583 sci_set_marker_at_line(doc->editor->sci, line - 1, MARKER_EXECUTE);
584 }
585
threads_mark(GeanyDocument * doc)586 void threads_mark(GeanyDocument *doc)
587 {
588 if (doc->real_path)
589 store_foreach(store, (GFunc) thread_iter_mark, doc);
590 }
591
threads_clear(void)592 void threads_clear(void)
593 {
594 store_foreach(store, (GFunc) thread_iter_unmark, GINT_TO_POINTER(TRUE));
595 store_clear(groups);
596 store_clear(store);
597 set_gdb_thread(NULL, FALSE);
598 thread_count = 0;
599 }
600
threads_delta(ScintillaObject * sci,const char * real_path,gint start,gint delta)601 void threads_delta(ScintillaObject *sci, const char *real_path, gint start, gint delta)
602 {
603 GtkTreeIter iter;
604 gboolean valid = scp_tree_store_get_iter_first(store, &iter);
605
606 while (valid)
607 {
608 const char *file;
609 gint line;
610
611 scp_tree_store_get(store, &iter, THREAD_FILE, &file, THREAD_LINE, &line, -1);
612
613 if (--line >= 0 && start <= line && !utils_filenamecmp(file, real_path))
614 utils_move_mark(sci, line, start, delta, MARKER_EXECUTE);
615
616 valid = scp_tree_store_iter_next(store, &iter);
617 }
618 }
619
threads_update(void)620 gboolean threads_update(void)
621 {
622 debug_send_command(N, "04-thread-info");
623 return TRUE;
624 }
625
thread_query_frame(char token)626 void thread_query_frame(char token)
627 {
628 debug_send_format(T, "0%c%s-stack-info-frame", token, thread_id);
629 }
630
thread_synchronize(void)631 void thread_synchronize(void)
632 {
633 if (thread_id && g_strcmp0(thread_id, gdb_thread))
634 debug_send_format(N, "04-thread-select %s", thread_id);
635 }
636
on_thread_selection_changed(GtkTreeSelection * selection,G_GNUC_UNUSED gpointer gdata)637 static void on_thread_selection_changed(GtkTreeSelection *selection,
638 G_GNUC_UNUSED gpointer gdata)
639 {
640 GtkTreeIter iter;
641
642 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
643 {
644 const gchar *state;
645 gint line;
646 const char *addr;
647
648 scp_tree_store_get(store, &iter, THREAD_ID, &thread_id, THREAD_STATE, &state,
649 THREAD_LINE, &line, THREAD_ADDR, &addr, -1);
650
651 if (strcmp(state, STOPPED))
652 {
653 thread_state = *state ? THREAD_RUNNING : THREAD_BLANK;
654 }
655 else if (addr)
656 {
657 if (line)
658 thread_state = THREAD_AT_SOURCE;
659 else
660 {
661 thread_state = THREAD_AT_ASSEMBLER;
662 view_dirty(VIEW_CONSOLE);
663 }
664 }
665 else
666 {
667 thread_state = THREAD_STOPPED;
668
669 if (debug_state() & DS_DEBUG)
670 thread_query_frame('4');
671 else
672 thread_state = THREAD_QUERY_FRAME;
673 }
674
675 frame_id = "0";
676 }
677 else
678 {
679 thread_id = frame_id = NULL;
680 thread_state = THREAD_BLANK;
681 }
682
683 views_data_dirty(debug_state());
684 update_state(debug_state()); /* may be a different state */
685 }
686
thread_seek_selected(gboolean focus)687 static void thread_seek_selected(gboolean focus)
688 {
689 view_seek_selected(selection, focus, SK_DEFAULT);
690 }
691
on_thread_refresh(G_GNUC_UNUSED const MenuItem * menu_item)692 static void on_thread_refresh(G_GNUC_UNUSED const MenuItem *menu_item)
693 {
694 debug_send_command(N, "-thread-info");
695 }
696
on_thread_unsorted(G_GNUC_UNUSED const MenuItem * menu_item)697 static void on_thread_unsorted(G_GNUC_UNUSED const MenuItem *menu_item)
698 {
699 scp_tree_store_set_sort_column_id(store, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
700 GTK_SORT_ASCENDING);
701 }
702
on_thread_view_source(G_GNUC_UNUSED const MenuItem * menu_item)703 static void on_thread_view_source(G_GNUC_UNUSED const MenuItem *menu_item)
704 {
705 thread_seek_selected(FALSE);
706 }
707
on_thread_synchronize(const MenuItem * menu_item)708 static void on_thread_synchronize(const MenuItem *menu_item)
709 {
710 if (menu_item)
711 debug_send_command(N, "02-thread-info");
712 else if (thread_id)
713 debug_send_format(N, "-thread-select %s", thread_id);
714 else
715 plugin_blink();
716 }
717
718 #ifdef G_OS_UNIX
send_signal(int sig)719 static void send_signal(int sig)
720 {
721 GtkTreeIter iter;
722
723 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
724 {
725 const char *pid;
726
727 scp_tree_store_get(store, &iter, THREAD_PID, &pid, -1);
728 if (kill(atoi(pid), sig) == -1)
729 show_errno("kill(pid)");
730 }
731 else
732 plugin_beep();
733 }
734
on_thread_interrupt(G_GNUC_UNUSED const MenuItem * menu_item)735 static void on_thread_interrupt(G_GNUC_UNUSED const MenuItem *menu_item)
736 {
737 send_signal(SIGINT); /* -exec-interrupt signals geany/scope */
738 }
739
on_thread_terminate(G_GNUC_UNUSED const MenuItem * menu_item)740 static void on_thread_terminate(G_GNUC_UNUSED const MenuItem *menu_item)
741 {
742 send_signal(SIGTERM);
743 }
744
745 #ifndef NSIG
746 #define NSIG 0xFF
747 #endif
748
on_thread_send_signal(G_GNUC_UNUSED const MenuItem * menu_item)749 static void on_thread_send_signal(G_GNUC_UNUSED const MenuItem *menu_item)
750 {
751 gdouble value = 1;
752
753 if (dialogs_show_input_numeric(_("Send Signal"), _("Enter signal #:"), &value, 1, NSIG,
754 1))
755 {
756 send_signal(value);
757 }
758 }
759 #else /* G_OS_UNIX */
iter_to_handle(GtkTreeIter * iter)760 static HANDLE iter_to_handle(GtkTreeIter *iter)
761 {
762 const char *pid;
763 HANDLE hid;
764
765 scp_tree_store_get(store, iter, THREAD_PID, &pid, -1);
766 hid = OpenProcess(PROCESS_ALL_ACCESS, FALSE, atoi(pid));
767 if (!hid)
768 show_errno("OpenProcess");
769
770 return hid;
771 }
772
on_thread_interrupt(G_GNUC_UNUSED const MenuItem * menu_item)773 static void on_thread_interrupt(G_GNUC_UNUSED const MenuItem *menu_item)
774 {
775 GtkTreeIter iter;
776 HANDLE hid;
777
778 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
779 {
780 hid = iter_to_handle(&iter);
781
782 if (hid)
783 {
784 if (!DebugBreakProcess(hid))
785 show_errno("DebugBreakProcess");
786 CloseHandle(hid);
787 }
788 }
789 }
790
on_thread_terminate(G_GNUC_UNUSED const MenuItem * menu_item)791 static void on_thread_terminate(G_GNUC_UNUSED const MenuItem *menu_item)
792 {
793 gdouble value = 1;
794
795 if (dialogs_show_input_numeric(_("Terminate Process"), _("Enter exit code:"), &value, 1,
796 UINT_MAX, 1))
797 {
798 GtkTreeIter iter;
799
800 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
801 {
802 HANDLE hid = iter_to_handle(&iter);
803
804 if (hid)
805 {
806 if (!TerminateProcess(hid, value))
807 show_errno("TerminateProcess");
808 CloseHandle(hid);
809 }
810 }
811 else
812 plugin_beep();
813 }
814 }
815 #endif /* G_OS_UNIX */
816
817 gboolean thread_show_group;
818
on_thread_show_group(const MenuItem * menu_item)819 static void on_thread_show_group(const MenuItem *menu_item)
820 {
821 on_menu_update_boolean(menu_item);
822 view_column_set_visible("thread_group_id_column", thread_show_group);
823 }
824
825 gboolean thread_show_core;
826
on_thread_show_core(const MenuItem * menu_item)827 static void on_thread_show_core(const MenuItem *menu_item)
828 {
829 on_menu_update_boolean(menu_item);
830 view_column_set_visible("thread_core_column", thread_show_core);
831 }
832
833 #define DS_VIEWABLE (DS_ACTIVE | DS_EXTRA_2)
834 #define DS_SIGNABLE (DS_ACTIVE | DS_EXTRA_3)
835
836 static MenuItem thread_menu_items[] =
837 {
838 { "thread_refresh", on_thread_refresh, DS_SENDABLE, NULL, NULL },
839 { "thread_unsorted", on_thread_unsorted, 0, NULL, NULL },
840 { "thread_view_source", on_thread_view_source, DS_VIEWABLE, NULL, NULL },
841 { "thread_synchronize", on_thread_synchronize, DS_SENDABLE, NULL, NULL },
842 { "thread_interrupt", on_thread_interrupt, DS_SIGNABLE, NULL, NULL },
843 { "thread_terminate", on_thread_terminate, DS_SIGNABLE, NULL, NULL },
844 #ifdef G_OS_UNIX
845 { "thread_send_signal", on_thread_send_signal, DS_SIGNABLE, NULL, NULL },
846 #endif
847 { "thread_auto_select", on_menu_display_booleans, 0, NULL, GINT_TO_POINTER(4) },
848 { "thread_select_on_running", on_menu_update_boolean, 0, NULL, &thread_select_on_running },
849 { "thread_select_on_stopped", on_menu_update_boolean, 0, NULL, &thread_select_on_stopped },
850 { "thread_select_on_exited", on_menu_update_boolean, 0, NULL, &thread_select_on_exited },
851 { "thread_select_follow", on_menu_update_boolean, 0, NULL, &thread_select_follow },
852 { "thread_show_columns", on_menu_display_booleans, 0, NULL, GINT_TO_POINTER(2) },
853 { "thread_show_group", on_thread_show_group, 0, NULL, &thread_show_group },
854 { "thread_show_core", on_thread_show_core, 0, NULL, &thread_show_core },
855 { NULL, NULL, 0, NULL, NULL }
856 };
857
thread_menu_extra_state(void)858 static guint thread_menu_extra_state(void)
859 {
860 GtkTreeIter iter;
861
862 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
863 {
864 const char *pid, *file;
865
866 scp_tree_store_get(store, &iter, THREAD_PID, &pid, THREAD_FILE, &file, -1);
867 return ((file != NULL) << DS_INDEX_2) | ((utils_atoi0(pid) > 0) << DS_INDEX_3);
868 }
869
870 return 0;
871 }
872
873 static MenuInfo thread_menu_info = { thread_menu_items, thread_menu_extra_state, 0 };
874
on_thread_synchronize_button_release(GtkWidget * widget,GdkEventButton * event,GtkWidget * menu)875 static void on_thread_synchronize_button_release(GtkWidget *widget, GdkEventButton *event,
876 GtkWidget *menu)
877 {
878 menu_shift_button_release(widget, event, menu, on_thread_synchronize);
879 }
880
thread_init(void)881 void thread_init(void)
882 {
883 GtkTreeView *tree = view_create("thread_view", &store, &selection);
884 GtkWidget *menu = menu_select("thread_menu", &thread_menu_info, selection);
885
886 view_set_sort_func(store, THREAD_ID, store_gint_compare);
887 view_set_sort_func(store, THREAD_FILE, store_seek_compare);
888 view_set_line_data_func("thread_line_column", "thread_line", THREAD_LINE);
889 view_set_sort_func(store, THREAD_PID, thread_ident_compare);
890 view_set_sort_func(store, THREAD_GROUP_ID, thread_ident_compare);
891 view_set_sort_func(store, THREAD_TARGET_ID, thread_ident_compare);
892 gtk_widget_set_has_tooltip(GTK_WIDGET(tree), TRUE);
893 g_signal_connect(tree, "query-tooltip", G_CALLBACK(on_view_query_base_tooltip),
894 get_column("thread_base_name_column"));
895
896 groups = SCP_TREE_STORE(get_object("thread_group_store"));
897 scp_tree_store_set_sort_column_id(groups, GROUP_ID, GTK_SORT_ASCENDING);
898 RUNNING = _("Running");
899 STOPPED = _("Stopped");
900 g_signal_connect(tree, "key-press-event", G_CALLBACK(on_view_key_press),
901 thread_seek_selected);
902 g_signal_connect(tree, "button-press-event", G_CALLBACK(on_view_button_1_press),
903 thread_seek_selected);
904
905 g_signal_connect(selection, "changed", G_CALLBACK(on_thread_selection_changed), NULL);
906 g_signal_connect(get_widget("thread_synchronize"), "button-release-event",
907 G_CALLBACK(on_thread_synchronize_button_release), menu);
908 #ifndef G_OS_UNIX
909 gtk_widget_hide(get_widget("thread_send_signal"));
910 #endif
911 }
912
thread_finalize(void)913 void thread_finalize(void)
914 {
915 store_foreach(store, (GFunc) thread_iter_unmark, NULL);
916 set_gdb_thread(NULL, FALSE);
917 }
918