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