]> gitweb.factorcode.org Git - factor.git/blob - vm/debug.cpp
VM: fix the debug printing so that instead of always printing to std::cout, you suppl...
[factor.git] / vm / debug.cpp
1 #include "master.hpp"
2
3 using namespace std;
4
5 namespace factor {
6
7 ostream& operator<<(ostream& out, const string* str) {
8   for (cell i = 0; i < string_capacity(str); i++)
9     out << (char)str->data()[i];
10   return out;
11 }
12
13 void factor_vm::print_word(ostream& out, word* word, cell nesting) {
14   if (tagged<object>(word->vocabulary).type_p(STRING_TYPE))
15     out << untag<string>(word->vocabulary) << ":";
16
17   if (tagged<object>(word->name).type_p(STRING_TYPE))
18     out << untag<string>(word->name);
19   else {
20     out << "#<not a string: ";
21     print_nested_obj(out, word->name, nesting);
22     out << ">";
23   }
24 }
25
26 void factor_vm::print_factor_string(ostream& out, string* str) {
27   out << '"' << str << '"';
28 }
29
30 void factor_vm::print_array(ostream& out, array* array, cell nesting) {
31   cell length = array_capacity(array);
32   cell i;
33   bool trimmed;
34
35   if (length > 10 && !full_output) {
36     trimmed = true;
37     length = 10;
38   } else
39     trimmed = false;
40
41   for (i = 0; i < length; i++) {
42     out << " ";
43     print_nested_obj(out, array_nth(array, i), nesting);
44   }
45
46   if (trimmed)
47     out << "...";
48 }
49
50 void factor_vm::print_alien(ostream& out, alien* alien, cell nesting) {
51   if (to_boolean(alien->expired))
52     out << "#<expired alien>";
53   else if (to_boolean(alien->base)) {
54     out << "#<displaced alien " << alien->displacement << "+";
55     print_nested_obj(out, alien->base, nesting);
56     out << ">";
57   } else {
58     out << "#<alien " << (void*)alien->address << ">";
59   }
60 }
61
62 void factor_vm::print_byte_array(ostream& out, byte_array* array, cell nesting) {
63   cell length = array->capacity;
64   cell i;
65   bool trimmed;
66   unsigned char* data = array->data<unsigned char>();
67
68   if (length > 16 && !full_output) {
69     trimmed = true;
70     length = 16;
71   } else
72     trimmed = false;
73
74   for (i = 0; i < length; i++) {
75     out << " " << (unsigned) data[i];
76   }
77
78   if (trimmed)
79     out << "...";
80 }
81
82 void factor_vm::print_tuple(ostream& out, tuple* tuple, cell nesting) {
83   tuple_layout* layout = untag<tuple_layout>(tuple->layout);
84   cell length = to_cell(layout->size);
85
86   out << " ";
87   print_nested_obj(out, layout->klass, nesting);
88
89   bool trimmed;
90   if (length > 10 && !full_output) {
91     trimmed = true;
92     length = 10;
93   } else
94     trimmed = false;
95
96   for (cell i = 0; i < length; i++) {
97     out << " ";
98     print_nested_obj(out, tuple->data()[i], nesting);
99   }
100
101   if (trimmed)
102     out << "...";
103 }
104
105 void factor_vm::print_nested_obj(ostream& out, cell obj, fixnum nesting) {
106   if (nesting <= 0 && !full_output) {
107     out << " ... ";
108     return;
109   }
110
111   quotation* quot;
112
113   switch (tagged<object>(obj).type()) {
114     case FIXNUM_TYPE:
115       out << untag_fixnum(obj);
116       break;
117     case FLOAT_TYPE:
118       out << untag_float(obj);
119       break;
120     case WORD_TYPE:
121       print_word(out, untag<word>(obj), nesting - 1);
122       break;
123     case STRING_TYPE:
124       print_factor_string(out, untag<string>(obj));
125       break;
126     case F_TYPE:
127       out << "f";
128       break;
129     case TUPLE_TYPE:
130       out << "T{";
131       print_tuple(out, untag<tuple>(obj), nesting - 1);
132       out << " }";
133       break;
134     case WRAPPER_TYPE:
135       out << "W{ ";
136       print_nested_obj(out, untag<wrapper>(obj)->object, nesting - 1);
137       out << " }";
138       break;
139     case BYTE_ARRAY_TYPE:
140       out << "B{";
141       print_byte_array(out, untag<byte_array>(obj), nesting - 1);
142       out << " }";
143       break;
144     case ARRAY_TYPE:
145       out << "{";
146       print_array(out, untag<array>(obj), nesting - 1);
147       out << " }";
148       break;
149     case QUOTATION_TYPE:
150       out << "[";
151       quot = untag<quotation>(obj);
152       print_array(out, untag<array>(quot->array), nesting - 1);
153       out << " ]";
154       break;
155     case ALIEN_TYPE:
156       print_alien(out, untag<alien>(obj), nesting - 1);
157       break;
158     default:
159       out << "#<" << type_name(tagged<object>(obj).type()) << " @ ";
160       out << (void*)obj << ">";
161       break;
162   }
163   out << flush;
164 }
165
166 void factor_vm::print_obj(ostream& out, cell obj) {
167   print_nested_obj(out, obj, 10);
168 }
169
170 void factor_vm::print_objects(ostream& out, cell* start, cell* end) {
171   for (; start <= end; start++) {
172     print_obj(out, *start);
173     cout << endl;
174   }
175 }
176
177 void factor_vm::print_datastack(ostream& out) {
178   out << "==== DATA STACK:" << endl;
179   if (ctx)
180     print_objects(out,
181                   (cell*)ctx->datastack_seg->start,
182                   (cell*)ctx->datastack);
183   else
184     out << "*** Context not initialized" << endl;
185 }
186
187 void factor_vm::print_retainstack(ostream& out) {
188   out << "==== RETAIN STACK:" << endl;
189   if (ctx)
190     print_objects(out,
191                   (cell*)ctx->retainstack_seg->start,
192                   (cell*)ctx->retainstack);
193   else
194     out << "*** Context not initialized" << endl;
195 }
196
197 struct stack_frame_printer {
198   factor_vm* parent;
199   ostream& out;
200
201   explicit stack_frame_printer(factor_vm* parent, ostream& out)
202       : parent(parent), out(out) {}
203   void operator()(cell frame_top, cell size, code_block* owner, cell addr) {
204     out << endl;
205     out << "frame: " << (void*)frame_top << " size " << size << endl;
206     out << "executing: ";
207     parent->print_obj(out, owner->owner);
208     out << endl;
209     out << "scan: ";
210     parent->print_obj(out, owner->scan(parent, addr));
211     out << endl;
212     out << "word/quot addr: ";
213     out << hex << owner->owner << dec;
214     out << endl;
215     out << "word/quot xt: ";
216     out << hex << owner->entry_point() << dec;
217     out << endl;
218     out << "return address: ";
219     out << hex << addr << dec;
220     out << endl;
221   }
222 };
223
224 void factor_vm::print_callstack(ostream& out) {
225   out << "==== CALL STACK:" << endl;
226   if (ctx) {
227     stack_frame_printer printer(this, out);
228     iterate_callstack(ctx, printer);
229   } else
230     out << "*** Context not initialized" << endl;
231 }
232
233 void factor_vm::print_callstack_object(ostream& out, callstack* obj) {
234   stack_frame_printer printer(this, out);
235   iterate_callstack_object(obj, printer);
236 }
237
238 struct padded_address {
239   cell value;
240
241   explicit padded_address(cell value) : value(value) {}
242 };
243
244 ostream& operator<<(ostream& out, const padded_address& value) {
245   char prev = out.fill('0');
246   out.width(sizeof(cell) * 2);
247   out << hex << value.value << dec;
248   out.fill(prev);
249   return out;
250 }
251
252 void factor_vm::dump_cell(ostream& out, cell x) {
253   out << padded_address(x) << ": ";
254   x = *(cell*)x;
255   out << padded_address(x) << " tag " << TAG(x) << endl;
256 }
257
258 void factor_vm::dump_memory(ostream& out, cell from, cell to) {
259   from = UNTAG(from);
260
261   for (; from <= to; from += sizeof(cell))
262     dump_cell(out, from);
263 }
264
265 template <typename Generation>
266 void factor_vm::dump_generation(ostream& out, const char* name, Generation* gen) {
267   out << name << ": ";
268   out << "Start=" << gen->start;
269   out << ", size=" << gen->size;
270   out << ", end=" << gen->end;
271   out << endl;
272 }
273
274 void factor_vm::dump_generations(ostream& out) {
275   out << hex;
276
277   dump_generation(out, "Nursery", &nursery);
278   dump_generation(out, "Aging", data->aging);
279   dump_generation(out, "Tenured", data->tenured);
280
281   out << "Cards:";
282   out << "base=" << (cell)data->cards << ", ";
283   out << "size=" << (cell)(data->cards_end - data->cards) << endl;
284   out << dec;
285 }
286
287 struct object_dumper {
288   factor_vm* parent;
289   cell type;
290   ostream& out;
291
292   object_dumper(factor_vm* parent, cell type, ostream& out)
293       : parent(parent), type(type), out(out) {}
294
295   void operator()(object* obj) {
296     if (type == TYPE_COUNT || obj->type() == type) {
297       out << padded_address((cell)obj) << " ";
298       parent->print_nested_obj(out, tag_dynamic(obj), 2);
299       out << endl;
300     }
301   }
302 };
303
304 void factor_vm::dump_objects(ostream& out, cell type) {
305   primitive_full_gc();
306   object_dumper dumper(this, type, out);
307   each_object(dumper);
308 }
309
310 struct find_data_reference_slot_visitor {
311   cell look_for;
312   object* obj;
313   factor_vm* parent;
314   ostream& out;
315
316   find_data_reference_slot_visitor(cell look_for,
317                                    object* obj,
318                                    factor_vm* parent,
319                                    ostream& out)
320     : look_for(look_for), obj(obj), parent(parent), out(out) {}
321
322   void operator()(cell* scan) {
323     if (look_for == *scan) {
324       out << padded_address((cell)obj) << " ";
325       parent->print_nested_obj(out, tag_dynamic(obj), 2);
326       out << endl;
327     }
328   }
329 };
330
331 struct dump_edges_slot_visitor {
332   object* obj;
333   factor_vm* parent;
334   ostream& out;
335
336   dump_edges_slot_visitor(cell, object* obj, factor_vm* parent, ostream& out)
337       : obj(obj), parent(parent), out(out) {}
338
339   void operator()(cell* scan) {
340     if (TAG(*scan) > F_TYPE)
341       out << (void*)tag_dynamic(obj) << " ==> " << (void*)*scan << endl;
342   }
343 };
344
345 template <typename SlotVisitor> struct data_reference_object_visitor {
346   cell look_for;
347   factor_vm* parent;
348   ostream& out;
349
350   data_reference_object_visitor(cell look_for, factor_vm* parent, ostream& out)
351       : look_for(look_for), parent(parent), out(out) {}
352
353   void operator()(object* obj) {
354     SlotVisitor visitor(look_for, obj, parent, out);
355     obj->each_slot(visitor);
356   }
357 };
358
359 void factor_vm::find_data_references(ostream& out, cell look_for) {
360   data_reference_object_visitor<find_data_reference_slot_visitor>
361       visitor(look_for, this, out);
362   each_object(visitor);
363 }
364
365 void factor_vm::dump_edges(ostream& out) {
366   data_reference_object_visitor<dump_edges_slot_visitor> visitor(0, this, out);
367   each_object(visitor);
368 }
369
370 struct code_block_printer {
371   factor_vm* parent;
372   ostream& out;
373   cell reloc_size, parameter_size;
374
375   explicit code_block_printer(factor_vm* parent, ostream& out)
376       : parent(parent), out(out), reloc_size(0), parameter_size(0) {}
377
378   void operator()(code_block* scan, cell size) {
379     const char* status;
380     if (scan->free_p())
381       status = "free";
382     else {
383       reloc_size += parent->object_size(scan->relocation);
384       parameter_size += parent->object_size(scan->parameters);
385
386       if (parent->code->allocator->state.marked_p((cell)scan))
387         status = "marked";
388       else
389         status = "allocated";
390
391       out << hex << (cell)scan << dec << " ";
392       out << hex << size << dec << " ";
393       out << status << " ";
394       out << "stack frame " << scan->stack_frame_size();
395       out << endl;
396     }
397   }
398 };
399
400 /* Dump all code blocks for debugging */
401 void factor_vm::dump_code_heap(ostream& out) {
402   code_block_printer printer(this, out);
403   code->allocator->iterate(printer);
404   out << printer.reloc_size << " bytes used by relocation tables" << endl;
405   out << printer.parameter_size << " bytes used by parameter tables" << endl;
406 }
407
408 void factor_vm::factorbug_usage(bool advanced_p) {
409   cout << "Basic commands:" << endl;
410 #ifdef WINDOWS
411   cout << "  q ^Z             -- quit Factor" << endl;
412 #else
413   cout << "  q ^D             -- quit Factor" << endl;
414 #endif
415   cout << "  c                -- continue executing Factor - NOT SAFE"
416        << endl;
417   cout << "  t                -- throw exception in Factor - NOT SAFE"
418        << endl;
419   cout << "  .s .r .c         -- print data, retain, call stacks"
420        << endl;
421   if (advanced_p) {
422     cout << "  help             -- reprint this message" << endl;
423     cout << "Advanced commands:" << endl;
424     cout << "  e                -- dump environment" << endl;
425     cout << "  d <addr> <count> -- dump memory" << endl;
426     cout << "  u <addr>         -- dump object at tagged <addr>"
427          << endl;
428     cout << "  . <addr>         -- print object at tagged <addr>"
429          << endl;
430     cout << "  g                -- dump generations" << endl;
431     cout << "  ds dr            -- dump data, retain stacks" << endl;
432     cout << "  trim             -- toggle output trimming" << endl;
433     cout << "  data             -- data heap dump" << endl;
434     cout << "  words            -- words dump" << endl;
435     cout << "  tuples           -- tuples dump" << endl;
436     cout << "  edges            -- print all object-to-object references"
437          << endl;
438     cout << "  refs <addr>      -- find data heap references to object"
439          << endl;
440     cout << "  push <addr>      -- push object on data stack - NOT SAFE"
441          << endl;
442     cout << "  gc               -- trigger full GC - NOT SAFE"
443          << endl;
444     cout << "  compact-gc       -- trigger compacting GC - NOT SAFE"
445          << endl;
446     cout << "  code             -- code heap dump" << endl;
447     cout << "  abort            -- call abort()" << endl;
448     cout << "  breakpoint       -- trigger system breakpoint" << endl;
449   } else {
450     cout << "  help             -- full help, including advanced commands"
451          << endl;
452   }
453   cout << endl;
454 }
455
456 static void exit_fep(factor_vm* vm) {
457   vm->unlock_console();
458   vm->handle_ctrl_c();
459   vm->fep_p = false;
460 }
461
462 void factor_vm::factorbug() {
463   if (fep_disabled) {
464     cout << "Low level debugger disabled" << endl;
465     exit(1);
466   }
467
468   if (sampling_profiler_p)
469     end_sampling_profiler();
470
471   fep_p = true;
472
473   cout << "Starting low level debugger..." << endl;
474
475   // Even though we've stopped the VM, the stdin_loop thread (see os-*.cpp)
476   // that pumps the console is still running concurrently. We lock a mutex so
477   // the thread will take a break and give us exclusive access to stdin.
478   lock_console();
479   ignore_ctrl_c();
480
481   if (!fep_help_was_shown) {
482     factorbug_usage(false);
483     fep_help_was_shown = true;
484   }
485   bool seen_command = false;
486
487   for (;;) {
488     std::string cmd;
489
490     cout << "> " << flush;
491
492     cin >> setw(1024) >> cmd >> setw(0);
493     if (!cin.good()) {
494       if (!seen_command) {
495         /* If we exit with an EOF immediately, then
496            dump stacks. This is useful for builder and
497            other cases where Factor is run with stdin
498            redirected to /dev/null */
499         fep_disabled = true;
500
501         print_datastack(cout);
502         print_retainstack(cout);
503         print_callstack(cout);
504       }
505
506       exit(1);
507     }
508
509     seen_command = true;
510
511     if (cmd == "q")
512       exit(1);
513     if (cmd == "d") {
514       cell addr = read_cell_hex();
515       if (cin.peek() == ' ')
516         cin.ignore();
517
518       if (!cin.good())
519         break;
520       cell count = read_cell_hex();
521       dump_memory(cout, addr, addr + count);
522     } else if (cmd == "u") {
523       cell addr = read_cell_hex();
524       cell count = object_size(addr);
525       dump_memory(cout, addr, addr + count);
526     } else if (cmd == ".") {
527       cell addr = read_cell_hex();
528       print_obj(cout, addr);
529       cout << endl;
530     } else if (cmd == "trim")
531       full_output = !full_output;
532     else if (cmd == "ds")
533       dump_memory(cout, ctx->datastack_seg->start, ctx->datastack);
534     else if (cmd == "dr")
535       dump_memory(cout, ctx->retainstack_seg->start, ctx->retainstack);
536     else if (cmd == ".s")
537       print_datastack(cout);
538     else if (cmd == ".r")
539       print_retainstack(cout);
540     else if (cmd == ".c")
541       print_callstack(cout);
542     else if (cmd == "e") {
543       for (cell i = 0; i < special_object_count; i++)
544         dump_cell(cout, (cell)&special_objects[i]);
545     } else if (cmd == "g")
546       dump_generations(cout);
547     else if (cmd == "c") {
548       exit_fep(this);
549       return;
550     } else if (cmd == "t") {
551       exit_fep(this);
552       general_error(ERROR_INTERRUPT, false_object, false_object);
553       FACTOR_ASSERT(false);
554     } else if (cmd == "data")
555       dump_objects(cout, TYPE_COUNT);
556     else if (cmd == "edges")
557       dump_edges(cout);
558     else if (cmd == "refs") {
559       cell addr = read_cell_hex();
560       cout << "Data heap references:" << endl;
561       find_data_references(cout, addr);
562       cout << endl;
563     } else if (cmd == "words")
564       dump_objects(cout, WORD_TYPE);
565     else if (cmd == "tuples")
566       dump_objects(cout, TUPLE_TYPE);
567     else if (cmd == "push") {
568       cell addr = read_cell_hex();
569       ctx->push(addr);
570     } else if (cmd == "code")
571       dump_code_heap(cout);
572     else if (cmd == "compact-gc")
573       primitive_compact_gc();
574     else if (cmd == "gc")
575       primitive_full_gc();
576     else if (cmd == "compact-gc")
577       primitive_compact_gc();
578     else if (cmd == "help")
579       factorbug_usage(true);
580     else if (cmd == "abort")
581       abort();
582     else if (cmd == "breakpoint")
583       breakpoint();
584     else
585       cout << "unknown command" << endl;
586   }
587 }
588
589 void factor_vm::primitive_die() {
590   cout << "The die word was called by the library. Unless you called it "
591       "yourself," << endl;
592   cout << "you have triggered a bug in Factor. Please report."
593        << endl;
594   factorbug();
595 }
596
597 }