1 /*
2  *  stack.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 #include <string.h>
21 
22 #include "common.h"
23 
24 enum
25 {
26 	STACK_ID,
27 	STACK_FILE,
28 	STACK_LINE,
29 	STACK_BASE_NAME,
30 	STACK_FUNC,
31 	STACK_ARGS,
32 	STACK_ADDR,
33 	STACK_ENTRY
34 };
35 
36 static ScpTreeStore *store;
37 static GtkTreeSelection *selection;
38 
stack_node_location(const ParseNode * node,const char * fid)39 static void stack_node_location(const ParseNode *node, const char *fid)
40 {
41 	iff (node->type == PT_ARRAY, "stack: contains value")
42 	{
43 		GArray *nodes = (GArray *) node->value;
44 		const char *id = parse_find_value(nodes, "level");
45 
46 		iff (id, "no level")
47 		{
48 			ParseLocation loc;
49 			GtkTreeIter iter;
50 
51 			parse_location(nodes, &loc);
52 			scp_tree_store_append_with_values(store, &iter, NULL, STACK_ID, id,
53 				STACK_FILE, loc.file, STACK_LINE, loc.line, STACK_BASE_NAME,
54 				loc.base_name, STACK_FUNC, loc.func, STACK_ARGS, NULL, STACK_ADDR,
55 				loc.addr, STACK_ENTRY, !loc.func ||
56 				parse_mode_get(loc.func, MODE_ENTRY), -1);
57 			parse_location_free(&loc);
58 
59 			if (!g_strcmp0(id, fid))
60 				gtk_tree_selection_select_iter(selection, &iter);
61 		}
62 	}
63 }
64 
65 const char *frame_id = NULL;
66 
on_stack_frames(GArray * nodes)67 void on_stack_frames(GArray *nodes)
68 {
69 	if (!g_strcmp0(parse_grab_token(nodes), thread_id))
70 	{
71 		char *fid = g_strdup(frame_id);
72 
73 		stack_clear();
74 		parse_foreach(parse_lead_array(nodes), (GFunc) stack_node_location, fid);
75 		g_free(fid);
76 
77 		if (!frame_id)
78 		{
79 			GtkTreeIter iter;
80 
81 			if (store_find(store, &iter, STACK_ID, "0"))
82 				utils_tree_set_cursor(selection, &iter, -1);
83 		}
84 	}
85 }
86 
87 typedef struct _StackData
88 {
89 	GString *string;
90 	gboolean entry;
91 } StackData;
92 
append_argument_variable(const ParseNode * node,const StackData * sd)93 static void append_argument_variable(const ParseNode *node, const StackData *sd)
94 {
95 	iff (node->type == PT_ARRAY, "args: contains value")
96 	{
97 		ParseVariable var;
98 
99 		if (parse_variable((GArray *) node->value, &var, NULL) &&
100 			(sd->entry || !g_str_has_suffix(var.name, "@entry")))
101 		{
102 			if (sd->string->len)
103 				g_string_append(sd->string, ", ");
104 
105 			if (option_argument_names)
106 			{
107 				g_string_append_printf(sd->string,
108 					option_long_mr_format ? "%s = " : "%s=", var.name);
109 			}
110 			g_string_append(sd->string, var.display);
111 			parse_variable_free(&var);
112 		}
113 	}
114 }
115 
stack_node_arguments(const ParseNode * node,G_GNUC_UNUSED gpointer gdata)116 static void stack_node_arguments(const ParseNode *node, G_GNUC_UNUSED gpointer gdata)
117 {
118 	iff (node->type == PT_ARRAY, "stack-args: contains value")
119 	{
120 		GArray *nodes = (GArray *) node->value;
121 		const char *id = parse_find_value(nodes, "level");
122 
123 		nodes = parse_find_array(nodes, "args");
124 
125 		iff (id && nodes, "no level or args")
126 		{
127 			GtkTreeIter iter;
128 
129 			iff (store_find(store, &iter, STACK_ID, id), "%s: level not found", id)
130 			{
131 				StackData sd;
132 
133 				sd.string = g_string_sized_new(0xFF);
134 				scp_tree_store_get(store, &iter, STACK_ENTRY, &sd.entry, -1);
135 				parse_foreach(nodes, (GFunc) append_argument_variable, &sd);
136 				scp_tree_store_set(store, &iter, STACK_ARGS, sd.string->str, -1);
137 				g_string_free(sd.string, TRUE);
138 			}
139 		}
140 	}
141 }
142 
on_stack_arguments(GArray * nodes)143 void on_stack_arguments(GArray *nodes)
144 {
145 	if (!g_strcmp0(parse_grab_token(nodes), thread_id))
146 		parse_foreach(parse_lead_array(nodes), (GFunc) stack_node_arguments, NULL);
147 }
148 
on_stack_follow(GArray * nodes)149 void on_stack_follow(GArray *nodes)
150 {
151 	if (!g_strcmp0(parse_grab_token(nodes), thread_id))
152 	{
153 		const char *id = parse_find_value(parse_lead_array(nodes), "level");
154 
155 		iff (id, "no level")
156 		{
157 			GtkTreeIter iter;
158 
159 			iff (store_find(store, &iter, STACK_ID, id), "%s: level not found", id)
160 				utils_tree_set_cursor(selection, &iter, 0.5);
161 		}
162 	}
163 }
164 
stack_entry(void)165 gboolean stack_entry(void)
166 {
167 	GtkTreeIter iter;
168 	gboolean entry = NULL;
169 
170 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
171 	{
172 		scp_tree_store_get(store, &iter, STACK_ENTRY, &entry, -1);
173 	}
174 	return entry;
175 }
176 
stack_clear(void)177 void stack_clear(void)
178 {
179 	store_clear(store);
180 }
181 
stack_send_update(char token)182 static void stack_send_update(char token)
183 {
184 	debug_send_format(T, "0%c%s-stack-list-frames", token, thread_id);
185 	debug_send_format(T, "0%c%s-stack-list-arguments 1", token, thread_id);
186 }
187 
stack_update(void)188 gboolean stack_update(void)
189 {
190 	stack_send_update('4');
191 	return TRUE;
192 }
193 
on_stack_selection_changed(GtkTreeSelection * selection,G_GNUC_UNUSED gpointer gdata)194 static void on_stack_selection_changed(GtkTreeSelection *selection,
195 	G_GNUC_UNUSED gpointer gdata)
196 {
197 	GtkTreeIter iter;
198 
199 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
200 		scp_tree_store_get(store, &iter, STACK_ID, &frame_id, -1);
201 	else
202 		frame_id = NULL;
203 
204 	views_context_dirty(debug_state(), TRUE);
205 }
206 
stack_seek_selected(gboolean focus)207 static void stack_seek_selected(gboolean focus)
208 {
209 	view_seek_selected(selection, focus, SK_DEFAULT);
210 }
211 
on_stack_refresh(G_GNUC_UNUSED const MenuItem * menu_item)212 static void on_stack_refresh(G_GNUC_UNUSED const MenuItem *menu_item)
213 {
214 	stack_send_update('2');
215 }
216 
on_stack_synchronize(const MenuItem * menu_item)217 static void on_stack_synchronize(const MenuItem *menu_item)
218 {
219 	if (menu_item)
220 		thread_query_frame('2');
221 	else if (frame_id)
222 		debug_send_format(T, "-stack-select-frame %s", frame_id);
223 	else
224 		plugin_blink();
225 }
226 
on_stack_unsorted(G_GNUC_UNUSED const MenuItem * menu_item)227 static void on_stack_unsorted(G_GNUC_UNUSED const MenuItem *menu_item)
228 {
229 	scp_tree_store_set_sort_column_id(store, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
230 		GTK_SORT_ASCENDING);
231 }
232 
on_stack_view_source(G_GNUC_UNUSED const MenuItem * menu_item)233 static void on_stack_view_source(G_GNUC_UNUSED const MenuItem *menu_item)
234 {
235 	stack_seek_selected(FALSE);
236 }
237 
238 gboolean stack_show_address;
239 
on_stack_show_address(const MenuItem * menu_item)240 static void on_stack_show_address(const MenuItem *menu_item)
241 {
242 	on_menu_update_boolean(menu_item);
243 	view_column_set_visible("stack_addr_column", stack_show_address);
244 }
245 
246 typedef struct _EntryData
247 {
248 	const char *func;
249 	gboolean entry;
250 	gint count;
251 } EntryData;
252 
stack_iter_show_entry(GtkTreeIter * iter,EntryData * ed)253 static void stack_iter_show_entry(GtkTreeIter *iter, EntryData *ed)
254 {
255 	const char *func;
256 
257 	scp_tree_store_get(store, iter, STACK_FUNC, &func, -1);
258 
259 	if (func && !strcmp(func, ed->func))
260 	{
261 		scp_tree_store_set(store, iter, STACK_ENTRY, ed->entry, -1);
262 		ed->count++;
263 	}
264 }
265 
on_stack_show_entry(const MenuItem * menu_item)266 static void on_stack_show_entry(const MenuItem *menu_item)
267 {
268 	EntryData ed = { NULL,
269 		gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item->widget)), 0 };
270 	GtkTreeIter iter;
271 
272 	view_dirty(VIEW_LOCALS);
273 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
274 	{
275 		scp_tree_store_get(store, &iter, STACK_FUNC, &ed.func, -1);
276 		parse_mode_update(ed.func, MODE_ENTRY, ed.entry);
277 		store_foreach(store, (GFunc) stack_iter_show_entry, &ed);
278 
279 		if (ed.count == 1)
280 		{
281 			debug_send_format(T, "04%s-stack-list-arguments 1 %s %s", thread_id, frame_id,
282 				frame_id);
283 		}
284 		else
285 		{
286 			debug_send_format(T, "04%s-stack-list-arguments 1", thread_id);
287 		}
288 	}
289 }
290 
291 #define DS_VIEWABLE (DS_ACTIVE | DS_EXTRA_2)
292 #define DS_ENTRABLE (DS_ACTIVE | DS_EXTRA_3)
293 
294 static MenuItem stack_menu_items[] =
295 {
296 	{ "stack_refresh",      on_stack_refresh,      DS_DEBUG,    NULL, NULL },
297 	{ "stack_unsorted",     on_stack_unsorted,     0,           NULL, NULL },
298 	{ "stack_view_source",  on_stack_view_source,  DS_VIEWABLE, NULL, NULL },
299 	{ "stack_synchronize",  on_stack_synchronize,  DS_DEBUG,    NULL, NULL },
300 	{ "stack_show_entry",   on_stack_show_entry,   DS_ENTRABLE, NULL, NULL },
301 	{ "stack_show_address", on_stack_show_address, 0,           NULL, &stack_show_address },
302 	{ NULL, NULL, 0, NULL, NULL }
303 };
304 
stack_menu_extra_state(void)305 static guint stack_menu_extra_state(void)
306 {
307 	GtkTreeIter iter;
308 
309 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
310 	{
311 		const char *file, *func;
312 		scp_tree_store_get(store, &iter, STACK_FILE, &file, STACK_FUNC, &func, -1);
313 		return ((file != NULL) << DS_INDEX_2) | ((func != NULL) << DS_INDEX_3);
314 	}
315 
316 	return 0;
317 }
318 
319 static MenuInfo stack_menu_info = { stack_menu_items, stack_menu_extra_state, 0 };
320 
on_stack_menu_show(G_GNUC_UNUSED GtkWidget * widget,const MenuItem * menu_item)321 static void on_stack_menu_show(G_GNUC_UNUSED GtkWidget *widget, const MenuItem *menu_item)
322 {
323 	GtkTreeIter iter;
324 
325 	if (gtk_tree_selection_get_selected(selection, NULL, &iter))
326 	{
327 		gboolean entry;
328 		scp_tree_store_get(store, &iter, STACK_ENTRY, &entry, -1);
329 		menu_item_set_active(menu_item, entry);
330 	}
331 	menu_item_set_active(menu_item + 1, stack_show_address);
332 }
333 
on_stack_synchronize_button_release(GtkWidget * widget,GdkEventButton * event,GtkWidget * menu)334 static void on_stack_synchronize_button_release(GtkWidget *widget, GdkEventButton *event,
335 	GtkWidget *menu)
336 {
337 	menu_shift_button_release(widget, event, menu, on_stack_synchronize);
338 }
339 
stack_init(void)340 void stack_init(void)
341 {
342 	GtkTreeView *tree = view_create("stack_view", &store, &selection);
343 	GtkWidget *menu = menu_select("stack_menu", &stack_menu_info, selection);
344 
345 	view_set_sort_func(store, STACK_ID, store_gint_compare);
346 	view_set_sort_func(store, STACK_FILE, store_seek_compare);
347 	view_set_line_data_func("stack_line_column", "stack_line", STACK_LINE);
348 	gtk_widget_set_has_tooltip(GTK_WIDGET(tree), TRUE);
349 	g_signal_connect(tree, "query-tooltip", G_CALLBACK(on_view_query_base_tooltip),
350 		get_column("stack_base_name_column"));
351 
352 	g_signal_connect(tree, "key-press-event", G_CALLBACK(on_view_key_press),
353 		stack_seek_selected);
354 	g_signal_connect(tree, "button-press-event", G_CALLBACK(on_view_button_1_press),
355 		stack_seek_selected);
356 	g_signal_connect(selection, "changed", G_CALLBACK(on_stack_selection_changed), NULL);
357 
358 	g_signal_connect(menu, "show", G_CALLBACK(on_stack_menu_show),
359 		(gpointer) menu_item_find(stack_menu_items, "stack_show_entry"));
360 	g_signal_connect(get_widget("stack_synchronize"), "button-release-event",
361 		G_CALLBACK(on_stack_synchronize_button_release), menu);
362 }
363