Branch data Line data Source code
1 : : // Debugging support for Bro policy files.
2 : :
3 : : #include "config.h"
4 : :
5 : : #include <stdio.h>
6 : : #include <stdarg.h>
7 : : #include <signal.h>
8 : : #include <ctype.h>
9 : :
10 : : #include <string>
11 : : using namespace std;
12 : :
13 : : #include "util.h"
14 : : #include "Debug.h"
15 : : #include "DebugCmds.h"
16 : : #include "DbgBreakpoint.h"
17 : : #include "Stmt.h"
18 : : #include "Func.h"
19 : : #include "Scope.h"
20 : : #include "PolicyFile.h"
21 : :
22 : : #ifdef HAVE_READLINE
23 : : #include <readline/readline.h>
24 : : #include <readline/history.h>
25 : : #endif
26 : :
27 : : bool g_policy_debug = false;
28 : 6 : DebuggerState g_debugger_state;
29 : 3 : TraceState g_trace_state;
30 : 6 : PDict(Filemap) g_dbgfilemaps;
31 : :
32 : : // These variables are used only to decide whether or not to print the
33 : : // current context; you don't want to do it after a step or next
34 : : // command unless you've exited a function.
35 : : static bool step_or_next_pending = false;
36 : : static Frame* last_frame;
37 : :
38 : 3 : DebuggerState::DebuggerState()
39 : : {
40 : 3 : next_bp_id = next_watch_id = next_display_id = 1;
41 : 3 : BreakBeforeNextStmt(false);
42 : 3 : curr_frame_idx = 0;
43 : 3 : BreakFromSignal(false);
44 : :
45 : : // ### Don't choose this arbitrary size! Extend Frame.
46 : 3 : dbg_locals = new Frame(1024, /* func = */ 0, /* fn_args = */ 0);
47 : 3 : }
48 : :
49 : 3 : DebuggerState::~DebuggerState()
50 : : {
51 : 3 : Unref(dbg_locals);
52 : 3 : }
53 : :
54 : 0 : bool StmtLocMapping::StartsAfter(const StmtLocMapping* m2)
55 : : {
56 [ # # ]: 0 : if ( ! m2 )
57 : 0 : internal_error("Assertion failed: %s", "m2 != 0");
58 : :
59 : : return loc.first_line > m2->loc.first_line ||
60 : : (loc.first_line == m2->loc.first_line &&
61 [ # # ][ # # ]: 0 : loc.first_column > m2->loc.first_column);
[ # # ]
62 : : }
63 : :
64 : :
65 : : // Generic debug message output.
66 : 0 : int debug_msg(const char* fmt, ...)
67 : : {
68 : : va_list args;
69 : : int retval;
70 : :
71 : 0 : va_start(args, fmt);
72 : 0 : retval = vfprintf(stderr, fmt, args);
73 : 0 : va_end(args);
74 : :
75 : 0 : return retval;
76 : : }
77 : :
78 : :
79 : : // Trace message output
80 : :
81 : 0 : FILE* TraceState::SetTraceFile(const char* filename)
82 : : {
83 : : FILE* newfile;
84 : :
85 [ # # ]: 0 : if ( streq(filename, "-") )
86 : 0 : newfile = stderr;
87 : : else
88 : 0 : newfile = fopen(filename, "w");
89 : :
90 : 0 : FILE* oldfile = trace_file;
91 [ # # ]: 0 : if ( newfile )
92 : : {
93 : 0 : trace_file = newfile;
94 : : }
95 : : else
96 : : {
97 : 0 : fprintf(stderr, "Unable to open trace file %s\n", filename);
98 : 0 : trace_file = 0;
99 : : }
100 : :
101 : 0 : return oldfile;
102 : : }
103 : :
104 : 0 : void TraceState::TraceOn()
105 : : {
106 : 0 : fprintf(stderr, "Execution tracing ON.\n");
107 : 0 : dbgtrace = true;
108 : 0 : }
109 : :
110 : 0 : void TraceState::TraceOff()
111 : : {
112 : 0 : fprintf(stderr, "Execution tracing OFF.\n");
113 : 0 : dbgtrace = false;
114 : 0 : }
115 : :
116 : 0 : int TraceState::LogTrace(const char* fmt, ...)
117 : : {
118 : : va_list args;
119 : : int retval;
120 : :
121 : 0 : va_start(args, fmt);
122 : :
123 : : // Prefix includes timestamp and file/line info.
124 : 0 : fprintf(trace_file, "%.6f ", network_time);
125 : :
126 : : const Stmt* stmt;
127 : 0 : Location loc;
128 : 0 : loc.filename = 0;
129 : :
130 [ # # # # ]: 0 : if ( g_frame_stack.size() > 0 && g_frame_stack.back() )
[ # # ]
131 : : {
132 : 0 : stmt = g_frame_stack.back()->GetNextStmt();
133 [ # # ]: 0 : if ( stmt )
134 : 0 : loc = *stmt->GetLocationInfo();
135 : : else
136 : : {
137 : 0 : const BroFunc* f = g_frame_stack.back()->GetFunction();
138 [ # # ]: 0 : if ( f )
139 : 0 : loc = *f->GetLocationInfo();
140 : : }
141 : : }
142 : :
143 [ # # ]: 0 : if ( ! loc.filename )
144 : : {
145 : 0 : loc.filename = "<no filename>";
146 : 0 : loc.last_line = 0;
147 : : }
148 : :
149 : 0 : fprintf(trace_file, "%s:%d", loc.filename, loc.last_line);
150 : :
151 : : // Each stack frame is indented.
152 [ # # ]: 0 : for ( int i = 0; i < int(g_frame_stack.size()); ++i )
153 : 0 : fprintf(trace_file, "\t");
154 : :
155 : 0 : retval = vfprintf(trace_file, fmt, args);
156 : :
157 : 0 : fflush(trace_file);
158 : 0 : va_end(args);
159 : :
160 : 0 : return retval;
161 : : }
162 : :
163 : :
164 : : // Helper functions.
165 : 0 : void get_first_statement(Stmt* list, Stmt*& first, Location& loc)
166 : : {
167 [ # # ]: 0 : if ( ! list )
168 : : {
169 : 0 : first = 0;
170 : 0 : return;
171 : : }
172 : :
173 : 0 : first = list;
174 [ # # ]: 0 : while ( first->Tag() == STMT_LIST )
175 : : {
176 [ # # ]: 0 : if ( first->AsStmtList()->Stmts()[0] )
177 : 0 : first = first->AsStmtList()->Stmts()[0];
178 : : else
179 : 0 : break;
180 : : }
181 : :
182 : 0 : loc = *first->GetLocationInfo();
183 : : }
184 : :
185 : : static void parse_function_name(vector<ParseLocationRec>& result,
186 : 0 : ParseLocationRec& plr, const string& s)
187 : : { // function name
188 : 0 : ID* id = lookup_ID(s.c_str(), current_module.c_str());
189 [ # # ]: 0 : if ( ! id )
190 : : {
191 : 0 : string fullname = make_full_var_name(current_module.c_str(), s.c_str());
192 : 0 : debug_msg("Function %s not defined.\n", fullname.c_str());
193 : 0 : plr.type = plrUnknown;
194 : 0 : return;
195 : : }
196 : :
197 : : FuncType* ftype;
198 [ # # ]: 0 : if ( ! (ftype = id->Type()->AsFuncType()) )
199 : : {
200 : 0 : debug_msg("Function %s not declared.\n", id->Name());
201 : 0 : plr.type = plrUnknown;
202 : 0 : return;
203 : : }
204 : :
205 [ # # ]: 0 : if ( ! id->HasVal() )
206 : : {
207 : 0 : debug_msg("Function %s declared but not defined.\n", id->Name());
208 : 0 : plr.type = plrUnknown;
209 : 0 : return;
210 : : }
211 : :
212 : 0 : const Func* func = id->ID_Val()->AsFunc();
213 : 0 : const vector<Func::Body>& bodies = func->GetBodies();
214 : :
215 [ # # ]: 0 : if ( bodies.size() == 0 )
216 : : {
217 : 0 : debug_msg("Function %s is a built-in function\n", id->Name());
218 : 0 : plr.type = plrUnknown;
219 : 0 : return;
220 : : }
221 : :
222 : 0 : Stmt* body = 0; // the particular body we care about; 0 = all
223 : :
224 [ # # ]: 0 : if ( bodies.size() == 1 )
225 : 0 : body = bodies[0].stmts;
226 : : else
227 : : {
228 [ # # # ]: 0 : while ( 1 )
229 : : {
230 : : debug_msg("There are multiple definitions of that event handler.\n"
231 : 0 : "Please choose one of the following options:\n");
232 [ # # ]: 0 : for ( unsigned int i = 0; i < bodies.size(); ++i )
233 : : {
234 : : Stmt* first;
235 : 0 : Location stmt_loc;
236 : : get_first_statement(bodies[i].stmts, first,
237 : 0 : stmt_loc);
238 : 0 : debug_msg("[%d] %s:%d\n", i+1, stmt_loc.filename, stmt_loc.first_line);
239 : : }
240 : :
241 : 0 : debug_msg("[a] All of the above\n");
242 : 0 : debug_msg("[n] None of the above\n");
243 : 0 : debug_msg("Enter your choice: ");
244 : :
245 : : char charinput[256];
246 [ # # ]: 0 : if ( ! fgets(charinput, sizeof(charinput) - 1, stdin) )
247 : : {
248 : 0 : plr.type = plrUnknown;
249 : 0 : return;
250 : : }
251 : :
252 [ # # ]: 0 : if ( charinput[strlen(charinput) - 1] == '\n' )
253 : 0 : charinput[strlen(charinput) - 1] = 0;
254 : :
255 : 0 : string input = charinput;
256 : :
257 [ # # ]: 0 : if ( input == "a" )
258 : 0 : break;
259 : :
260 [ # # ]: 0 : if ( input == "n" )
261 : : {
262 : 0 : plr.type = plrUnknown;
263 : 0 : return;
264 : : }
265 : :
266 : 0 : int option = atoi(input.c_str());
267 [ # # # # ]: 0 : if ( option > 0 && option <= (int) bodies.size() )
[ # # ]
268 : : {
269 : 0 : body = bodies[option - 1].stmts;
270 : : break;
271 : : }
272 : : }
273 : : }
274 : :
275 : 0 : plr.type = plrFunction;
276 : :
277 : : // Find first atomic (non-STMT_LIST) statement
278 : : Stmt* first;
279 : 0 : Location stmt_loc;
280 : :
281 [ # # ]: 0 : if ( body )
282 : : {
283 : 0 : get_first_statement(body, first, stmt_loc);
284 [ # # ]: 0 : if ( first )
285 : : {
286 : 0 : plr.stmt = first;
287 : 0 : plr.filename = stmt_loc.filename;
288 : 0 : plr.line = stmt_loc.last_line;
289 : : }
290 : : }
291 : :
292 : : else
293 : : {
294 : 0 : result.pop_back();
295 : : ParseLocationRec plr;
296 : :
297 [ # # ]: 0 : for ( unsigned int i = 0; i < bodies.size(); ++i )
298 : : {
299 : 0 : get_first_statement(bodies[i].stmts, first, stmt_loc);
300 [ # # ]: 0 : if ( ! first )
301 : 0 : continue;
302 : :
303 : 0 : plr.type = plrFunction;
304 : 0 : plr.stmt = first;
305 : 0 : plr.filename = stmt_loc.filename;
306 : 0 : plr.line = stmt_loc.last_line;
307 : 0 : result.push_back(plr);
308 : : }
309 : 0 : }
310 : : }
311 : :
312 : 0 : vector<ParseLocationRec> parse_location_string(const string& s)
313 : : {
314 : 0 : vector<ParseLocationRec> result;
315 : 0 : result.push_back(ParseLocationRec());
316 : 0 : ParseLocationRec& plr = result[0];
317 : 0 : const char* full_filename = 0;
318 : :
319 : : // If plrFileAndLine, set this to the filename you want; for
320 : : // memory management reasons, the real filename is set when looking
321 : : // up the line number to find the corresponding statement.
322 : 0 : const char* loc_filename = 0;
323 : :
324 [ # # ]: 0 : if ( sscanf(s.c_str(), "%d", &plr.line) )
325 : : { // just a line number (implicitly referring to the current file)
326 : 0 : loc_filename = g_debugger_state.last_loc.filename;
327 : 0 : plr.type = plrFileAndLine;
328 : : }
329 : :
330 : : else
331 : : {
332 : 0 : string::size_type pos_colon = s.find(':');
333 : 0 : string::size_type pos_dblcolon = s.find("::");
334 : :
335 [ # # # # ]: 0 : if ( pos_colon == string::npos || pos_dblcolon != string::npos )
336 : 0 : parse_function_name(result, plr, s);
337 : : else
338 : : { // file:line
339 : 0 : string filename = s.substr(0, pos_colon);
340 : 0 : string line_string = s.substr(pos_colon + 1, s.length() - pos_colon);
341 : :
342 [ # # ]: 0 : if ( ! sscanf(line_string.c_str(), "%d", &plr.line) )
343 : 0 : plr.type = plrUnknown;
344 : :
345 : : FILE* throwaway = search_for_file(filename.c_str(), "bro",
346 : 0 : &full_filename);
347 [ # # ]: 0 : if ( ! throwaway )
348 : : {
349 : 0 : debug_msg("No such policy file: %s.\n", filename.c_str());
350 : 0 : plr.type = plrUnknown;
351 : 0 : return result;
352 : : }
353 : :
354 : 0 : fclose(throwaway);
355 : :
356 : 0 : loc_filename = full_filename;
357 [ # # ][ # # ]: 0 : plr.type = plrFileAndLine;
358 : : }
359 : : }
360 : :
361 [ # # ]: 0 : if ( plr.type == plrFileAndLine )
362 : : {
363 : 0 : Filemap* map = g_dbgfilemaps.Lookup(loc_filename);
364 [ # # ]: 0 : if ( ! map )
365 : : internal_error("Policy file %s should have been loaded\n",
366 : 0 : loc_filename);
367 : :
368 [ # # ]: 0 : if ( plr.line > how_many_lines_in(loc_filename) )
369 : : {
370 : 0 : debug_msg("No line %d in %s.\n", plr.line, loc_filename);
371 [ # # ]: 0 : delete [] full_filename;
372 : 0 : plr.type = plrUnknown;
373 : 0 : return result;
374 : : }
375 : :
376 : 0 : StmtLocMapping* hit = 0;
377 [ # # ]: 0 : loop_over_queue(*map, i)
378 : : {
379 : 0 : StmtLocMapping* entry = (*map)[i];
380 : 0 : plr.filename = (*map)[i]->Loc().filename;
381 : :
382 [ # # ]: 0 : if ( entry->Loc().first_line > plr.line )
383 : 0 : break;
384 : :
385 [ # # ][ # # ]: 0 : if ( plr.line >= entry->Loc().first_line &&
[ # # ]
386 : : plr.line <= entry->Loc().last_line )
387 : : {
388 : 0 : hit = (*map)[i];
389 : 0 : break;
390 : : }
391 : : }
392 : :
393 [ # # ]: 0 : if ( hit )
394 : 0 : plr.stmt = hit->Statement();
395 : : else
396 : 0 : plr.stmt = 0;
397 : : }
398 : :
399 [ # # ]: 0 : delete [] full_filename;
400 : 0 : return result;
401 : : }
402 : :
403 : :
404 : : // Interactive debugging console.
405 : :
406 : : static int dbg_dispatch_cmd(DebugCmd cmd_code, const vector<string>& args);
407 : :
408 : : #ifdef HAVE_READLINE
409 : :
410 : : void using_history(void);
411 : :
412 : : static bool init_readline()
413 : : {
414 : : // ### Set up custom completion.
415 : :
416 : : rl_outstream = stderr;
417 : : using_history();
418 : :
419 : : return false;
420 : : }
421 : :
422 : : #endif
423 : :
424 : 0 : void break_signal(int)
425 : : {
426 : 0 : g_debugger_state.BreakBeforeNextStmt(true);
427 : 0 : g_debugger_state.BreakFromSignal(true);
428 : 0 : }
429 : :
430 : 0 : int dbg_init_debugger(const char* cmdfile)
431 : : {
432 [ # # ]: 0 : if ( ! g_policy_debug )
433 : 0 : return 0; // probably shouldn't have been called
434 : :
435 : 0 : init_global_dbg_constants();
436 : :
437 : : // Hit the debugger before running anything.
438 : 0 : g_debugger_state.BreakBeforeNextStmt(true);
439 : :
440 [ # # ]: 0 : if ( cmdfile )
441 : : // ### Implement this
442 : 0 : debug_msg("Command files not supported. Using interactive mode.\n");
443 : :
444 : : // ### if ( interactive ) (i.e., not reading cmds from a file)
445 : : #ifdef HAVE_READLINE
446 : : init_readline();
447 : : #endif
448 : :
449 : 0 : signal(SIGINT, &break_signal);
450 : 0 : signal(SIGTERM, break_signal);
451 : :
452 : 0 : return 1;
453 : : }
454 : :
455 : 0 : int dbg_shutdown_debugger()
456 : : {
457 : : // ### TODO: Remove signal handlers
458 : 0 : return 1;
459 : : }
460 : :
461 : :
462 : : // Umesh: I stole this code from libedit; I modified it here to use
463 : : // <string>s to avoid memory management problems. The main command is returned
464 : : // by the operation argument; the additional arguments are put in the
465 : : // supplied vector.
466 : : //
467 : : // Parse the string into individual tokens, similarily to how shell
468 : : // would do it.
469 : :
470 : 0 : void tokenize(const char* cstr, string& operation, vector<string>& arguments)
471 : : {
472 : 0 : int num_tokens = 0;
473 : 0 : char delim = '\0';
474 : 0 : const string str(cstr);
475 : :
476 [ # # ]: 0 : for ( int i = 0; i < (signed int) str.length(); ++i )
477 : : {
478 [ # # ]: 0 : while ( isspace((unsigned char) str[i]) )
479 : 0 : ++i;
480 : :
481 : 0 : int start = i;
482 : :
483 [ # # ]: 0 : for ( ; str[i]; ++i )
484 : : {
485 [ # # ]: 0 : if ( str[i] == '\\' )
486 : : {
487 [ # # ]: 0 : if ( i < (signed int) str.length() )
488 : 0 : ++i;
489 : : }
490 : :
491 [ # # ][ # # ]: 0 : else if ( ! delim && (str[i] == '\'' || str[i] == '"') )
[ # # ][ # # ]
492 : 0 : delim = str[i];
493 : :
494 [ # # ][ # # ]: 0 : else if ( delim && str[i] == delim )
[ # # ]
495 : : {
496 : 0 : delim = '\0';
497 : 0 : ++i;
498 : 0 : break;
499 : : }
500 : :
501 [ # # ][ # # ]: 0 : else if ( ! delim && isspace(str[i]) )
[ # # ]
502 : 0 : break;
503 : : }
504 : :
505 : 0 : size_t len = i - start;
506 : :
507 [ # # ]: 0 : if ( ! num_tokens )
508 : 0 : operation = string(str, start, len);
509 : : else
510 : 0 : arguments.push_back(string(str, start, len));
511 : :
512 : 0 : ++num_tokens;
513 : 0 : }
514 : 0 : }
515 : :
516 : :
517 : : // Given a command string, parse it and send the command to be dispatched.
518 : 0 : int dbg_execute_command(const char* cmd)
519 : : {
520 : 0 : bool matched_history = false;
521 : :
522 [ # # ]: 0 : if ( ! cmd )
523 : 0 : return 0;
524 : :
525 [ # # ]: 0 : if ( streq(cmd, "") ) // do the GDB command completion
526 : : {
527 : : #ifdef HAVE_READLINE
528 : : int i;
529 : : for ( i = history_length; i >= 1; --i )
530 : : {
531 : : HIST_ENTRY* entry = history_get(i);
532 : : if ( ! entry )
533 : : return 0;
534 : :
535 : : const DebugCmdInfo* info =
536 : : (const DebugCmdInfo*) entry->data;
537 : :
538 : : if ( info && info->Repeatable() )
539 : : {
540 : : cmd = entry->line;
541 : : matched_history = true;
542 : : break;
543 : : }
544 : : }
545 : : #endif
546 : :
547 [ # # ]: 0 : if ( ! matched_history )
548 : 0 : return 0;
549 : : }
550 : :
551 : 0 : char* localcmd = copy_string(cmd);
552 : :
553 : 0 : string opstring;
554 : 0 : vector<string> arguments;
555 : 0 : tokenize(localcmd, opstring, arguments);
556 : :
557 [ # # ]: 0 : delete [] localcmd;
558 : :
559 : : // Make sure we know this op name.
560 : 0 : const char* matching_cmds[num_debug_cmds()];
561 : 0 : int num_matches = find_all_matching_cmds(opstring, matching_cmds);
562 : :
563 [ # # ]: 0 : if ( ! num_matches )
564 : : {
565 : 0 : debug_msg("No Matching command for '%s'.\n", opstring.c_str());
566 : 0 : return 0;
567 : : }
568 : :
569 [ # # ]: 0 : if ( num_matches > 1 )
570 : : {
571 : 0 : debug_msg("Ambiguous command; could be\n");
572 : :
573 [ # # ]: 0 : for ( int i = 0; i < num_debug_cmds(); ++i )
574 [ # # ]: 0 : if ( matching_cmds[i] )
575 : 0 : debug_msg("\t%s\n", matching_cmds[i]);
576 : :
577 : 0 : return 0;
578 : : }
579 : :
580 : : // Matched exactly one command: find out which one.
581 : 0 : DebugCmd cmd_code = dcInvalid;
582 [ # # ]: 0 : for ( int i = 0; i < num_debug_cmds(); ++i )
583 [ # # ]: 0 : if ( matching_cmds[i] )
584 : : {
585 : 0 : cmd_code = (DebugCmd) i;
586 : 0 : break;
587 : : }
588 : :
589 : : #ifdef HAVE_READLINE
590 : : // Insert command into history.
591 : : if ( ! matched_history && cmd && *cmd )
592 : : {
593 : : /* The prototype for add_history(), at least under MacOS,
594 : : * has it taking a char* rather than a const char*.
595 : : * But documentation at
596 : : * http://tiswww.case.edu/php/chet/readline/history.html
597 : : * suggests that it's safe to assume it's really const char*.
598 : : */
599 : : add_history((char *) cmd);
600 : : HISTORY_STATE* state = history_get_history_state();
601 : : state->entries[state->length-1]->data = (histdata_t *) get_debug_cmd_info(cmd_code);
602 : : }
603 : : #endif
604 : :
605 [ # # ]: 0 : if ( int(cmd_code) >= num_debug_cmds() )
606 : 0 : internal_error("Assertion failed: %s", "int(cmd_code) < num_debug_cmds()");
607 : :
608 : : // Dispatch to the op-specific handler (with args).
609 : 0 : int retcode = dbg_dispatch_cmd(cmd_code, arguments);
610 [ # # ]: 0 : if ( retcode < 0 )
611 : 0 : return retcode;
612 : :
613 : 0 : const DebugCmdInfo* info = get_debug_cmd_info(cmd_code);
614 [ # # ]: 0 : if ( ! info )
615 : 0 : internal_error("Assertion failed: %s", "info");
616 : :
617 [ # # ]: 0 : if ( ! info )
618 : 0 : return -2; // ### yuck, why -2?
619 : :
620 : 0 : return info->ResumeExecution();
621 : : }
622 : :
623 : : // Call the appropriate function for the command.
624 : 0 : static int dbg_dispatch_cmd(DebugCmd cmd_code, const vector<string>& args)
625 : : {
626 [ # # # # # : 0 : switch ( cmd_code ) {
# # # # #
# # # # #
# # ]
627 : : case dcHelp:
628 : 0 : dbg_cmd_help(cmd_code, args);
629 : 0 : break;
630 : :
631 : : case dcQuit:
632 : 0 : debug_msg("Program Terminating\n");
633 : 0 : exit(0);
634 : :
635 : : case dcNext:
636 : 0 : g_frame_stack.back()->BreakBeforeNextStmt(true);
637 : 0 : step_or_next_pending = true;
638 : 0 : last_frame = g_frame_stack.back();
639 : 0 : break;
640 : :
641 : : case dcStep:
642 : 0 : g_debugger_state.BreakBeforeNextStmt(true);
643 : 0 : step_or_next_pending = true;
644 : 0 : last_frame = g_frame_stack.back();
645 : 0 : break;
646 : :
647 : : case dcContinue:
648 : 0 : g_debugger_state.BreakBeforeNextStmt(false);
649 : 0 : debug_msg("Continuing.\n");
650 : 0 : break;
651 : :
652 : : case dcFinish:
653 : 0 : g_frame_stack.back()->BreakOnReturn(true);
654 : 0 : g_debugger_state.BreakBeforeNextStmt(false);
655 : 0 : break;
656 : :
657 : : case dcBreak:
658 : 0 : dbg_cmd_break(cmd_code, args);
659 : 0 : break;
660 : :
661 : : case dcBreakCondition:
662 : 0 : dbg_cmd_break_condition(cmd_code, args);
663 : 0 : break;
664 : :
665 : : case dcDeleteBreak:
666 : : case dcClearBreak:
667 : : case dcDisableBreak:
668 : : case dcEnableBreak:
669 : : case dcIgnoreBreak:
670 : 0 : dbg_cmd_break_set_state(cmd_code, args);
671 : 0 : break;
672 : :
673 : : case dcPrint:
674 : 0 : dbg_cmd_print(cmd_code, args);
675 : 0 : break;
676 : :
677 : : case dcBacktrace:
678 : 0 : return dbg_cmd_backtrace(cmd_code, args);
679 : :
680 : : case dcFrame:
681 : : case dcUp:
682 : : case dcDown:
683 : 0 : return dbg_cmd_frame(cmd_code, args);
684 : :
685 : : case dcInfo:
686 : 0 : return dbg_cmd_info(cmd_code, args);
687 : :
688 : : case dcList:
689 : 0 : return dbg_cmd_list(cmd_code, args);
690 : :
691 : : case dcDisplay:
692 : : case dcUndisplay:
693 : 0 : debug_msg("Command not yet implemented.\n");
694 : 0 : break;
695 : :
696 : : case dcTrace:
697 : 0 : return dbg_cmd_trace(cmd_code, args);
698 : :
699 : : default:
700 : : debug_msg("INTERNAL ERROR: "
701 : : "Got an unknown debugger command in DbgDispatchCmd: %d\n",
702 : 0 : cmd_code);
703 : 0 : return 0;
704 : : }
705 : :
706 : 0 : return 0;
707 : : }
708 : :
709 : 0 : static char* get_prompt(bool reset_counter = false)
710 : : {
711 : : static char prompt[512];
712 : : static int counter = 0;
713 : :
714 [ # # ]: 0 : if ( reset_counter )
715 : 0 : counter = 0;
716 : :
717 : 0 : safe_snprintf(prompt, sizeof(prompt), "(Bro [%d]) ", counter++);
718 : :
719 : 0 : return prompt;
720 : : }
721 : :
722 : 0 : string get_context_description(const Stmt* stmt, const Frame* frame)
723 : : {
724 : : char buf[1024];
725 : 0 : ODesc d;
726 : 0 : const BroFunc* func = frame->GetFunction();
727 : :
728 [ # # ]: 0 : if ( func )
729 : 0 : func->DescribeDebug(&d, frame->GetFuncArgs());
730 : : else
731 : 0 : d.Add("<unknown function>", 0);
732 : :
733 : 0 : Location loc;
734 [ # # ]: 0 : if ( stmt )
735 : 0 : loc = *stmt->GetLocationInfo();
736 : : else
737 : : {
738 : 0 : loc.filename = "<no filename>";
739 : 0 : loc.last_line = 0;
740 : : }
741 : :
742 : : safe_snprintf(buf, sizeof(buf), "In %s at %s:%d",
743 : 0 : d.Description(), loc.filename, loc.last_line);
744 : :
745 : 0 : return string(buf);
746 : : }
747 : :
748 : 0 : int dbg_handle_debug_input()
749 : : {
750 : : static char* input_line = 0;
751 : 0 : int status = 0;
752 : :
753 [ # # ]: 0 : if ( g_debugger_state.BreakFromSignal() )
754 : : {
755 : 0 : debug_msg("Program received signal SIGINT: entering debugger\n");
756 : :
757 : 0 : g_debugger_state.BreakFromSignal(false);
758 : : }
759 : :
760 : 0 : Frame* curr_frame = g_frame_stack.back();
761 : 0 : const BroFunc* func = curr_frame->GetFunction();
762 [ # # ]: 0 : if ( func )
763 : 0 : current_module = func->GetID()->ModuleName();
764 : : else
765 : 0 : current_module = GLOBAL_MODULE_NAME;
766 : :
767 : 0 : const Stmt* stmt = curr_frame->GetNextStmt();
768 [ # # ]: 0 : if ( ! stmt )
769 : 0 : internal_error("Assertion failed: %s", "stmt != 0");
770 : :
771 : 0 : const Location loc = *stmt->GetLocationInfo();
772 : :
773 [ # # # # ]: 0 : if ( ! step_or_next_pending || g_frame_stack.back() != last_frame )
[ # # ]
774 : : {
775 : : string context =
776 : 0 : get_context_description(stmt, g_frame_stack.back());
777 : 0 : debug_msg("%s\n", context.c_str());
778 : : }
779 : :
780 : 0 : step_or_next_pending = false;
781 : :
782 : : PrintLines(loc.filename, loc.first_line,
783 : 0 : loc.last_line - loc.first_line + 1, true);
784 : 0 : g_debugger_state.last_loc = loc;
785 : :
786 [ # # ]: 0 : do
787 : : {
788 : : // readline returns a pointer to a buffer it allocates; it's
789 : : // freed at the bottom.
790 : : #ifdef HAVE_READLINE
791 : : input_line = readline(get_prompt());
792 : : #else
793 : 0 : printf ("%s", get_prompt());
794 : :
795 : : // readline uses malloc, and we want to be consistent
796 : : // with it.
797 : 0 : input_line = (char*) safe_malloc(1024);
798 : 0 : input_line[1023] = 0;
799 : : // ### Maybe it's not always stdin.
800 : 0 : fgets(input_line, 1023, stdin);
801 : : #endif
802 : :
803 : : // ### Maybe not stdin; maybe do better cleanup.
804 [ # # ]: 0 : if ( feof(stdin) )
805 : 0 : exit(0);
806 : :
807 : 0 : status = dbg_execute_command(input_line);
808 : :
809 [ # # ]: 0 : if ( input_line )
810 : : {
811 : 0 : free(input_line); // this was malloc'ed
812 : 0 : input_line = 0;
813 : : }
814 : : else
815 : 0 : exit(0);
816 : : }
817 : : while ( status == 0 );
818 : :
819 : : // Clear out some state. ### Is there a better place?
820 : 0 : g_debugger_state.curr_frame_idx = 0;
821 : 0 : g_debugger_state.already_did_list = false;
822 : :
823 : 0 : signal(SIGINT, &break_signal);
824 : 0 : signal(SIGTERM, &break_signal);
825 : :
826 : 0 : return 0;
827 : : }
828 : :
829 : :
830 : : // Return true to continue execution, false to abort.
831 : 636210 : bool pre_execute_stmt(Stmt* stmt, Frame* f)
832 : : {
833 [ - + ][ # # ]: 636210 : if ( ! g_policy_debug ||
[ # # ][ + - ]
834 : : stmt->Tag() == STMT_LIST || stmt->Tag() == STMT_NULL )
835 : 636210 : return true;
836 : :
837 [ # # ]: 0 : if ( g_trace_state.DoTrace() )
838 : : {
839 : 0 : ODesc d;
840 : 0 : stmt->Describe(&d);
841 : :
842 : 0 : const char* desc = d.Description();
843 : 0 : const char* s = strchr(desc, '\n');
844 : :
845 : : int len;
846 [ # # ]: 0 : if ( s )
847 : 0 : len = s - desc;
848 : : else
849 : 0 : len = strlen(desc);
850 : :
851 : 0 : g_trace_state.LogTrace("%*s\n", len, desc);
852 : : }
853 : :
854 : 0 : bool should_break = false;
855 : :
856 [ # # ][ # # ]: 0 : if ( g_debugger_state.BreakBeforeNextStmt() ||
[ # # ]
857 : : f->BreakBeforeNextStmt() )
858 : : {
859 [ # # ]: 0 : if ( g_debugger_state.BreakBeforeNextStmt() )
860 : 0 : g_debugger_state.BreakBeforeNextStmt(false);
861 : :
862 [ # # ]: 0 : if ( f->BreakBeforeNextStmt() )
863 : 0 : f->BreakBeforeNextStmt(false);
864 : :
865 : 0 : should_break = true;
866 : : }
867 : :
868 [ # # ]: 0 : if ( stmt->BPCount() )
869 : : {
870 : 0 : pair<BPMapType::iterator, BPMapType::iterator> p;
871 : :
872 : 0 : p = g_debugger_state.breakpoint_map.equal_range(stmt);
873 : :
874 [ # # ]: 0 : if ( p.first == p.second )
875 : 0 : internal_error("Breakpoint count nonzero, but no matching breakpoints");
876 : :
877 [ # # ]: 0 : for ( BPMapType::iterator i = p.first; i != p.second; ++i )
878 : : {
879 : 0 : int break_code = i->second->ShouldBreak(stmt);
880 [ # # ]: 0 : if ( break_code == 2 ) // ### 2?
881 : : {
882 : 0 : i->second->SetEnable(false);
883 [ # # ]: 0 : delete i->second;
884 : : }
885 : :
886 [ # # ][ # # ]: 0 : should_break = should_break || break_code;
887 : : }
888 : : }
889 : :
890 [ # # ]: 0 : if ( should_break )
891 : 0 : dbg_handle_debug_input();
892 : :
893 : 636210 : return true;
894 : : }
895 : :
896 : 636210 : bool post_execute_stmt(Stmt* stmt, Frame* f, Val* result, stmt_flow_type* flow)
897 : : {
898 : : // Handle the case where someone issues a "next" debugger command,
899 : : // but we're at a return statement, so the next statement is in
900 : : // some other function.
901 [ + + ][ - + ]: 636210 : if ( *flow == FLOW_RETURN && f->BreakBeforeNextStmt() )
[ - + ]
902 : 0 : g_debugger_state.BreakBeforeNextStmt(true);
903 : :
904 : : // Handle "finish" commands.
905 [ + + ][ - + ]: 636210 : if ( *flow == FLOW_RETURN && f->BreakOnReturn() )
[ - + ]
906 : : {
907 [ # # ]: 0 : if ( result )
908 : : {
909 : 0 : ODesc d;
910 : 0 : result->Describe(&d);
911 : 0 : debug_msg("Return Value: '%s'\n", d.Description());
912 : : }
913 : : else
914 : 0 : debug_msg("Return Value: <none>\n");
915 : :
916 : 0 : g_debugger_state.BreakBeforeNextStmt(true);
917 : 0 : f->BreakOnReturn(false);
918 : : }
919 : :
920 : 636210 : return true;
921 : : }
922 : :
923 : :
924 : : // Evaluates the given expression in the context of the currently selected
925 : : // frame. Returns the resulting value, or nil if none (or there was an error).
926 : : Expr* g_curr_debug_expr = 0;
927 : :
928 : : // ### fix this hardwired access to external variables etc.
929 : : struct yy_buffer_state;
930 : : typedef struct yy_buffer_state* YY_BUFFER_STATE;
931 : : YY_BUFFER_STATE bro_scan_string(const char*);
932 : :
933 : : extern YYLTYPE yylloc; // holds start line and column of token
934 : : extern int line_number;
935 : : extern const char* filename;
936 : :
937 : 0 : Val* dbg_eval_expr(const char* expr)
938 : : {
939 : : // Push the current frame's associated scope.
940 : : // Note: g_debugger_state.curr_frame_idx is the user-visible number,
941 : : // while the array index goes in the opposite direction
942 : : int frame_idx =
943 : 0 : (g_frame_stack.size() - 1) - g_debugger_state.curr_frame_idx;
944 : :
945 [ # # # # ]: 0 : if ( ! (frame_idx >= 0 && (unsigned) frame_idx < g_frame_stack.size()) )
[ # # ]
946 : 0 : internal_error("Assertion failed: %s", "frame_idx >= 0 && (unsigned) frame_idx < g_frame_stack.size()");
947 : :
948 : 0 : Frame* frame = g_frame_stack[frame_idx];
949 [ # # ]: 0 : if ( ! (frame) )
950 : 0 : internal_error("Assertion failed: %s", "frame");
951 : :
952 : 0 : const BroFunc* func = frame->GetFunction();
953 [ # # ]: 0 : if ( func )
954 : 0 : push_existing_scope(func->GetScope());
955 : :
956 : : // ### Possibly push a debugger-local scope?
957 : :
958 : : // Set up the lexer to read from the string.
959 : 0 : string parse_string = string("@DEBUG ") + expr;
960 : 0 : bro_scan_string(parse_string.c_str());
961 : :
962 : : // Fix filename and line number for the lexer/parser, which record it.
963 : 0 : filename = "<interactive>";
964 : 0 : line_number = 1;
965 : 0 : yylloc.filename = filename;
966 : 0 : yylloc.first_line = yylloc.last_line = line_number = 1;
967 : :
968 : : // Parse the thing into an expr.
969 : 0 : Val* result = 0;
970 [ # # ]: 0 : if ( yyparse() )
971 : : {
972 [ # # ]: 0 : if ( g_curr_debug_expr )
973 : : {
974 [ # # ]: 0 : delete g_curr_debug_expr;
975 : 0 : g_curr_debug_expr = 0;
976 : : }
977 : : }
978 : : else
979 : 0 : result = g_curr_debug_expr->Eval(frame);
980 : :
981 [ # # ]: 0 : if ( func )
982 : 0 : pop_scope();
983 : :
984 [ # # ]: 0 : delete g_curr_debug_expr;
985 : 0 : g_curr_debug_expr = 0;
986 : :
987 : 0 : return result;
988 [ + - ][ + - ]: 6 : }
|