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