this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "debugging.h"
3
4#include <iomanip>
5#include <iostream>
6
7#include "bytearray-builtins.h"
8#include "bytecode.h"
9#include "bytes-builtins.h"
10#include "dict-builtins.h"
11#include "file.h"
12#include "frame.h"
13#include "handles.h"
14#include "runtime.h"
15#include "unicode.h"
16#include "vector.h"
17
18namespace py {
19
20static bool dumpSimple(std::ostream& os, RawObject value);
21
22static std::ostream& dumpBytecode(std::ostream& os, const Bytes& bytecode,
23 const char* indent) {
24 word num_opcodes = bytecodeLength(bytecode);
25 for (word i = 0; i < num_opcodes; i++) {
26 byte op = static_cast<byte>(bytecodeOpAt(bytecode, i));
27 byte arg = bytecodeArgAt(bytecode, i);
28 std::ios_base::fmtflags saved_flags = os.flags();
29 os << indent << " " << std::setw(4) << std::hex
30 << i * kCompilerCodeUnitSize << ' ';
31 os.flags(saved_flags);
32 os << kBytecodeNames[op] << " " << static_cast<unsigned>(arg) << '\n';
33 }
34 return os;
35}
36
37static std::ostream& dumpMutableBytecode(std::ostream& os,
38 const MutableBytes& bytecode,
39 const char* indent) {
40 word num_opcodes = rewrittenBytecodeLength(bytecode);
41 for (word i = 0; i < num_opcodes; i++) {
42 byte op = rewrittenBytecodeOpAt(bytecode, i);
43 byte arg = rewrittenBytecodeArgAt(bytecode, i);
44 uint16_t cache = rewrittenBytecodeCacheAt(bytecode, i);
45 std::ios_base::fmtflags saved_flags = os.flags();
46 os << indent << " " << std::setw(4) << std::hex << i * kCodeUnitSize
47 << ' ';
48 os << "[" << std::setw(4) << std::hex << cache << "] ";
49 os.flags(saved_flags);
50 os << kBytecodeNames[op] << " " << static_cast<unsigned>(arg) << '\n';
51 }
52 return os;
53}
54
55static void dumpCodeFlags(std::ostream& os, word flags) {
56 if (flags & Code::kOptimized) os << " optimized";
57 if (flags & Code::kNewlocals) os << " newlocals";
58 if (flags & Code::kVarargs) os << " varargs";
59 if (flags & Code::kVarkeyargs) os << " varkeyargs";
60 if (flags & Code::kNested) os << " nested";
61 if (flags & Code::kGenerator) os << " generator";
62 if (flags & Code::kNofree) os << " nofree";
63 if (flags & Code::kCoroutine) os << " coroutine";
64 if (flags & Code::kIterableCoroutine) os << " iterable_coroutine";
65 if (flags & Code::kAsyncGenerator) os << " async_generator";
66 if (flags & Code::kBuiltin) os << " builtin";
67}
68
69std::ostream& dumpExtendedCode(std::ostream& os, RawCode value,
70 const char* indent) {
71 HandleScope scope(Thread::current());
72 Code code(&scope, value);
73 os << "code " << code.name() << ":\n" << indent << " flags:";
74 dumpCodeFlags(os, code.flags());
75 os << '\n';
76 os << indent << " argcount: " << code.argcount() << '\n'
77 << indent << " posonlyargcount: " << code.posonlyargcount() << '\n'
78 << indent << " kwonlyargcount: " << code.kwonlyargcount() << '\n'
79 << indent << " nlocals: " << code.nlocals() << '\n'
80 << indent << " stacksize: " << code.stacksize() << '\n'
81 << indent << " filename: " << code.filename() << '\n'
82 << indent << " consts: " << code.consts() << '\n'
83 << indent << " names: " << code.names() << '\n'
84 << indent << " cellvars: " << code.cellvars() << '\n'
85 << indent << " freevars: " << code.freevars() << '\n'
86 << indent << " varnames: " << code.varnames() << '\n';
87 Object bytecode_obj(&scope, code.code());
88 if (bytecode_obj.isBytes()) {
89 Bytes bytecode(&scope, *bytecode_obj);
90 dumpBytecode(os, bytecode, indent);
91 }
92
93 return os;
94}
95
96std::ostream& dumpExtendedFunction(std::ostream& os, RawFunction value) {
97 HandleScope scope(Thread::current());
98 Function function(&scope, value);
99 os << "function " << function.name() << ":\n"
100 << " qualname: " << function.qualname() << '\n'
101 << " module: " << function.moduleName() << '\n'
102 << " annotations: " << function.annotations() << '\n'
103 << " closure: " << function.closure() << '\n'
104 << " defaults: " << function.defaults() << '\n'
105 << " kwdefaults: " << function.kwDefaults() << '\n'
106 << " intrinsic: " << function.intrinsic() << '\n'
107 << " dict: " << function.dict() << '\n'
108 << " flags:";
109 word flags = function.flags();
110 dumpCodeFlags(os, flags);
111 if (flags & Function::Flags::kSimpleCall) os << " simple_call";
112 if (flags & Function::Flags::kInterpreted) os << " interpreted";
113 if (flags & Function::Flags::kExtension) os << " extension";
114 if (flags & Function::Flags::kCompiled) os << " compiled";
115 os << '\n';
116
117 os << " code: ";
118 if (function.code().isCode()) {
119 dumpExtendedCode(os, Code::cast(function.code()), " ");
120 if (function.rewrittenBytecode().isMutableBytes()) {
121 MutableBytes bytecode(&scope, function.rewrittenBytecode());
122 os << " Rewritten bytecode:\n";
123 dumpMutableBytecode(os, bytecode, "");
124 }
125 } else {
126 os << function.code() << '\n';
127 }
128 return os;
129}
130
131std::ostream& dumpExtendedInstance(std::ostream& os, RawInstance value) {
132 Thread* thread = Thread::current();
133 HandleScope scope(thread);
134 Runtime* runtime = thread->runtime();
135 Instance instance(&scope, value);
136 LayoutId layout_id = instance.layoutId();
137 os << "heap object with layout " << static_cast<word>(layout_id);
138 Object layout_obj(&scope, runtime->layoutAtSafe(layout_id));
139 if (!layout_obj.isLayout()) {
140 os << '\n';
141 return os;
142 }
143 Layout layout(&scope, *layout_obj);
144 if (!runtime->isInstanceOfType(layout.describedType())) {
145 os << '\n';
146 return os;
147 }
148 Type type(&scope, layout.describedType());
149 os << " (" << type << "):\n";
150 Tuple in_object(&scope, layout.inObjectAttributes());
151 Tuple entry(&scope, runtime->emptyTuple());
152 for (word i = 0, length = in_object.length(); i < length; i++) {
153 entry = in_object.at(i);
154 AttributeInfo info(entry.at(1));
155 os << " (in-object) " << entry.at(0) << " = "
156 << instance.instanceVariableAt(info.offset()) << '\n';
157 }
158 if (layout.hasTupleOverflow()) {
159 Tuple overflow_attributes(&scope, layout.overflowAttributes());
160 Tuple overflow(&scope,
161 instance.instanceVariableAt(layout.overflowOffset()));
162 for (word i = 0, length = overflow_attributes.length(); i < length; i++) {
163 entry = overflow_attributes.at(i);
164 AttributeInfo info(entry.at(1));
165 os << " (overflow) " << entry.at(0) << " = "
166 << overflow.at(info.offset()) << '\n';
167 }
168 } else if (layout.hasDictOverflow()) {
169 word offset = layout.dictOverflowOffset();
170 os << " overflow dict: " << instance.instanceVariableAt(offset) << '\n';
171 }
172 return os;
173}
174
175std::ostream& dumpExtendedLayout(std::ostream& os, RawLayout value,
176 const char* indent) {
177 Thread* thread = Thread::current();
178 HandleScope scope(thread);
179 Layout layout(&scope, value);
180 os << indent << "layout " << static_cast<word>(layout.id()) << ":\n";
181 Object type(&scope, layout.describedType());
182 os << indent << " described type: " << type << '\n';
183 os << indent
184 << " num in-object attributes: " << layout.numInObjectAttributes()
185 << '\n';
186 Tuple in_object(&scope, layout.inObjectAttributes());
187 Runtime* runtime = thread->runtime();
188 Tuple entry(&scope, runtime->emptyTuple());
189 for (word i = 0, length = in_object.length(); i < length; i++) {
190 entry = in_object.at(i);
191 AttributeInfo info(entry.at(1));
192 os << indent << " " << entry.at(0) << " @ " << info.offset() << '\n';
193 }
194 if (layout.hasTupleOverflow()) {
195 os << indent << " overflow tuple:\n";
196 Tuple overflow_attributes(&scope, layout.overflowAttributes());
197 for (word i = 0, length = overflow_attributes.length(); i < length; i++) {
198 entry = overflow_attributes.at(i);
199 AttributeInfo info(entry.at(1));
200 os << indent << " " << entry.at(0) << " @ " << info.offset() << '\n';
201 }
202 } else if (layout.hasDictOverflow()) {
203 os << indent << " overflow dict @ " << layout.dictOverflowOffset() << '\n';
204 } else if (layout.isSealed()) {
205 os << indent << " sealed\n";
206 } else {
207 os << indent << " invalid overflow\n";
208 }
209 return os;
210}
211
212static void dumpTypeFlags(std::ostream& os, word flags) {
213 if (flags & Type::Flag::kIsAbstract) os << " abstract";
214 if (flags & Type::Flag::kHasCustomDict) os << " has_custom_dict";
215 if (flags & Type::Flag::kHasNativeData) os << " has_native_data";
216 if (flags & Type::Flag::kHasCycleGC) os << " has_cycle_gc";
217 if (flags & Type::Flag::kHasDefaultDealloc) os << " has_default_dealloc";
218 if (flags & Type::Flag::kHasSlots) os << " has_slots";
219 if (flags & Type::Flag::kIsFixedAttributeBase) {
220 os << " is_fixed_attribute_base";
221 }
222}
223
224std::ostream& dumpExtendedType(std::ostream& os, RawType value) {
225 Thread* thread = Thread::current();
226 HandleScope scope(thread);
227 Type type(&scope, value);
228
229 os << "type " << type.name() << ":\n";
230 os << " bases: " << type.bases() << '\n';
231 os << " mro: " << type.mro() << '\n';
232 os << " flags:";
233 dumpTypeFlags(os, type.flags());
234 os << '\n';
235 Object builtin_base_layout(
236 &scope, thread->runtime()->layoutAtSafe(type.builtinBase()));
237 os << " builtin base: ";
238 if (builtin_base_layout.isLayout()) {
239 os << builtin_base_layout << '\n';
240 } else {
241 os << "invalid layout\n";
242 }
243 if (type.instanceLayout().isLayout()) {
244 dumpExtendedLayout(os, Layout::cast(type.instanceLayout()), " ");
245 } else {
246 // I don't think this case should occur during normal operation, but maybe
247 // we dump a type that isn't completely initialized yet.
248 os << " layout: " << type.instanceLayout() << '\n';
249 }
250 return os;
251}
252
253// The functions in this file may be used during garbage collection, so this
254// function is used to approximate a read barrier until we have a better
255// solution.
256static RawObject checkForward(std::ostream& os, RawObject value) {
257 if (!value.isHeapObject()) return value;
258 RawHeapObject heap_obj = HeapObject::cast(value);
259 if (!heap_obj.isForwarding()) return value;
260 os << "<Forward to> ";
261 return heap_obj.forward();
262}
263
264static std::ostream& dumpObjectGeneric(std::ostream& os, RawObject object_raw) {
265 Thread* thread = Thread::current();
266 HandleScope scope(thread);
267 Object object(&scope, object_raw);
268 LayoutId id = object.layoutId();
269 Object layout(&scope, thread->runtime()->layoutAtSafe(id));
270 if (layout.isLayout()) {
271 Object type_obj(&scope, Layout::cast(*layout).describedType());
272 if (thread->runtime()->isInstanceOfType(*type_obj)) {
273 Type type(&scope, *type_obj);
274 Object name(&scope, type.name());
275 if (name.isStr()) {
276 return os << '<' << name << " object>";
277 }
278 }
279 }
280 return os << "<object with LayoutId " << static_cast<word>(id) << '>';
281}
282
283std::ostream& dumpExtended(std::ostream& os, RawObject value) {
284 value = checkForward(os, value);
285 LayoutId layout = value.layoutId();
286 switch (layout) {
287 case LayoutId::kCode:
288 return dumpExtendedCode(os, Code::cast(value), "");
289 case LayoutId::kFunction:
290 return dumpExtendedFunction(os, Function::cast(value));
291 case LayoutId::kLayout:
292 return dumpExtendedLayout(os, Layout::cast(value), "");
293 case LayoutId::kType:
294 return dumpExtendedType(os, Type::cast(value));
295 default:
296 if (dumpSimple(os, value)) {
297 return os << '\n';
298 }
299 if (value.isInstance()) {
300 return dumpExtendedInstance(os, Instance::cast(value));
301 }
302 dumpObjectGeneric(os, value);
303 return os << '\n';
304 }
305}
306
307std::ostream& operator<<(std::ostream& os, CastError value) {
308 switch (value) {
309 case CastError::None:
310 return os << "None";
311 case CastError::Underflow:
312 return os << "Underflow";
313 case CastError::Overflow:
314 return os << "Overflow";
315 }
316 return os << "<invalid>";
317}
318
319std::ostream& operator<<(std::ostream& os, RawBool value) {
320 return os << (value.value() ? "True" : "False");
321}
322
323std::ostream& operator<<(std::ostream& os, RawBoundMethod value) {
324 return os << "<bound_method " << value.function() << ", " << value.self()
325 << '>';
326}
327
328static void dumpBytes(std::ostream& os, RawBytes bytes, word length) {
329 os << "b\'";
330 for (word i = 0; i < length; i++) {
331 byte b = bytes.byteAt(i);
332 switch (b) {
333 case '\'':
334 os << "\\\'";
335 break;
336 case '\t':
337 os << "\\t";
338 break;
339 case '\n':
340 os << "\\n";
341 break;
342 case '\r':
343 os << "\\r";
344 break;
345 case '\\':
346 os << "\\\\";
347 break;
348 default:
349 if (ASCII::isPrintable(b)) {
350 os << static_cast<char>(b);
351 } else {
352 std::ios_base::fmtflags saved_flags = os.flags();
353 char saved_fill = os.fill('0');
354 os << "\\x" << std::setw(2) << std::hex << static_cast<unsigned>(b);
355 os.fill(saved_fill);
356 os.flags(saved_flags);
357 }
358 }
359 }
360 os << '\'';
361}
362
363std::ostream& operator<<(std::ostream& os, RawBytearray value) {
364 os << "bytearray(";
365 dumpBytes(os, Bytes::cast(value.items()), value.numItems());
366 return os << ')';
367}
368
369std::ostream& operator<<(std::ostream& os, RawBytes value) {
370 dumpBytes(os, value, value.length());
371 return os;
372}
373
374std::ostream& operator<<(std::ostream& os, RawCode value) {
375 return os << "<code " << value.name() << ">";
376}
377
378std::ostream& operator<<(std::ostream& os, RawDict value) {
379 Thread* thread = Thread::current();
380 HandleScope scope(thread);
381 Dict dict(&scope, value);
382 os << '{';
383 Object key(&scope, NoneType::object());
384 Object value_obj(&scope, NoneType::object());
385 const char* delimiter = "";
386 for (word i = 0; dictNextItem(dict, &i, &key, &value_obj);) {
387 os << delimiter << key << ": " << value_obj;
388 delimiter = ", ";
389 }
390 return os << '}';
391}
392
393std::ostream& operator<<(std::ostream& os, RawError value) {
394 os << "Error";
395 switch (value.kind()) {
396 case ErrorKind::kNone:
397 return os;
398 case ErrorKind::kException:
399 return os << "<Exception>";
400 case ErrorKind::kNotFound:
401 return os << "<NotFound>";
402 case ErrorKind::kOutOfBounds:
403 return os << "<OutOfBounds>";
404 case ErrorKind::kOutOfMemory:
405 return os << "<OutOfMemory>";
406 case ErrorKind::kNoMoreItems:
407 return os << "<NoMoreItems>";
408 }
409 return os << "<Invalid>";
410}
411
412std::ostream& operator<<(std::ostream& os, RawFloat value) {
413 std::ios_base::fmtflags saved = os.flags();
414 os << std::hexfloat << value.value();
415 os.flags(saved);
416 return os;
417}
418
419std::ostream& operator<<(std::ostream& os, RawFunction value) {
420 return os << "<function " << value.qualname() << '>';
421}
422
423std::ostream& operator<<(std::ostream& os, RawInt value) {
424 if (value.isSmallInt()) return os << SmallInt::cast(value);
425 if (value.isBool()) return os << Bool::cast(value);
426 return os << LargeInt::cast(value);
427}
428
429std::ostream& operator<<(std::ostream& os, RawLargeInt value) {
430 HandleScope scope(Thread::current());
431 LargeInt large_int(&scope, value);
432
433 os << "largeint([";
434 for (word i = 0, num_digits = large_int.numDigits(); i < num_digits; i++) {
435 uword digit = large_int.digitAt(i);
436 if (i > 0) {
437 os << ", ";
438 }
439 os << "0x";
440 std::ios_base::fmtflags saved_flags = os.flags();
441 char saved_fill = os.fill('0');
442 os << std::setw(16) << std::hex << digit;
443 os.fill(saved_fill);
444 os.flags(saved_flags);
445 }
446 return os << "])";
447}
448
449std::ostream& operator<<(std::ostream& os, RawLargeStr value) {
450 HandleScope scope(Thread::current());
451 Str str(&scope, value);
452 unique_c_ptr<char[]> data(str.toCStr());
453 os << '"';
454 os.write(data.get(), str.length());
455 return os << '"';
456}
457
458std::ostream& operator<<(std::ostream& os, RawLayout value) {
459 Thread* thread = Thread::current();
460 os << "<layout " << static_cast<word>(value.id());
461 if (thread->runtime()->isInstanceOfType(value.describedType())) {
462 HandleScope scope(Thread::current());
463 Type type(&scope, value.describedType());
464 os << " (" << type.name() << ')';
465 }
466 return os << '>';
467}
468
469std::ostream& operator<<(std::ostream& os, RawList value) {
470 HandleScope scope(Thread::current());
471 List list(&scope, value);
472 os << '[';
473 for (word i = 0, num_itesm = list.numItems(); i < num_itesm; i++) {
474 if (i > 0) os << ", ";
475 os << list.at(i);
476 }
477 return os << ']';
478}
479
480std::ostream& operator<<(std::ostream& os, RawModule value) {
481 return os << "<module " << value.name() << ">";
482}
483
484std::ostream& operator<<(std::ostream& os, RawNoneType) { return os << "None"; }
485
486std::ostream& operator<<(std::ostream& os, RawObject value) {
487 value = checkForward(os, value);
488 if (dumpSimple(os, value)) return os;
489 return dumpObjectGeneric(os, value);
490}
491
492std::ostream& operator<<(std::ostream& os, RawSmallInt value) {
493 return os << value.value();
494}
495
496std::ostream& operator<<(std::ostream& os, RawSmallStr value) {
497 HandleScope scope(Thread::current());
498 Str str(&scope, value);
499 byte buffer[RawSmallStr::kMaxLength];
500 word length = str.length();
501 DCHECK(static_cast<size_t>(length) <= sizeof(buffer), "Buffer too small");
502 str.copyTo(buffer, length);
503 os << '"';
504 os.write(reinterpret_cast<const char*>(buffer), length);
505 return os << '"';
506}
507
508std::ostream& operator<<(std::ostream& os, RawStr value) {
509 if (value.isSmallStr()) return os << SmallStr::cast(value);
510 return os << LargeStr::cast(value);
511}
512
513std::ostream& operator<<(std::ostream& os, RawTuple value) {
514 HandleScope scope(Thread::current());
515 Tuple tuple(&scope, value);
516 os << '(';
517 word length = tuple.length();
518 for (word i = 0; i < length; i++) {
519 if (i > 0) os << ", ";
520 os << tuple.at(i);
521 }
522 if (length == 1) os << ',';
523 return os << ')';
524}
525
526std::ostream& operator<<(std::ostream& os, RawMutableTuple value) {
527 HandleScope scope(Thread::current());
528 MutableTuple tuple(&scope, value);
529 os << "mutabletuple(";
530 word length = tuple.length();
531 for (word i = 0; i < length; i++) {
532 if (i > 0) os << ", ";
533 os << tuple.at(i);
534 }
535 if (length == 1) os << ',';
536 return os << ')';
537}
538
539std::ostream& operator<<(std::ostream& os, RawType value) {
540 return os << "<type " << value.name() << ">";
541}
542
543std::ostream& operator<<(std::ostream& os, RawValueCell value) {
544 os << "<value_cell ";
545 if (value.isPlaceholder()) {
546 os << "placeholder>";
547 } else {
548 os << '(' << value.value() << ")>";
549 }
550 return os;
551}
552
553std::ostream& operator<<(std::ostream& os, RawWeakLink value) {
554 os << std::hex;
555 os << "<_weaklink 0x" << value.raw() << " referent=" << value.referent()
556 << ", next=0x" << value.next().raw() << ", prev=0x" << value.prev().raw()
557 << ">";
558 os << std::dec;
559 return os;
560}
561
562static void dumpSingleFrame(Thread* thread, std::ostream& os, Frame* frame,
563 RawObject* stack_pointer) {
564 if (const char* invalid = frame->isInvalid()) {
565 os << "- invalid frame (" << invalid << ")\n";
566 return;
567 }
568
569 HandleScope scope(thread);
570
571 Tuple var_names(&scope, thread->runtime()->emptyTuple());
572 Tuple freevar_names(&scope, thread->runtime()->emptyTuple());
573 Tuple cellvar_names(&scope, thread->runtime()->emptyTuple());
574 bool output_pc = true;
575 word num_locals = 0;
576 if (frame->isSentinel()) {
577 os << "- initial frame\n";
578 } else if (!frame->function().isFunction()) {
579 os << "- function: <invalid>\n";
580 } else {
581 Function function(&scope, frame->function());
582 num_locals = frame->function().totalLocals();
583 os << "- function: " << function << '\n';
584 if (function.code().isCode()) {
585 Code code(&scope, function.code());
586 os << " code: " << code.name() << '\n';
587 if (code.isNative()) {
588 os << " pc: n/a (native)\n";
589 } else {
590 word pc = frame->virtualPC();
591 os << " pc: " << pc;
592
593 // Print filename and line number, if possible.
594 os << " (" << code.filename();
595 if (code.lnotab().isBytes()) {
596 os << ":" << code.offsetToLineNum(pc);
597 }
598 os << ")";
599 os << '\n';
600 }
601 output_pc = false;
602
603 if (code.varnames().isTuple()) {
604 var_names = code.varnames();
605 }
606 if (code.cellvars().isTuple()) {
607 cellvar_names = code.cellvars();
608 }
609 if (code.freevars().isTuple()) {
610 freevar_names = code.freevars();
611 }
612 }
613 }
614 if (output_pc) {
615 os << " pc: " << frame->virtualPC() << '\n';
616 }
617
618 // TODO(matthiasb): Also dump the block stack.
619 word var_names_length = var_names.length();
620 word cellvar_names_length = cellvar_names.length();
621 word freevar_names_length = freevar_names.length();
622 if (num_locals > 0) os << " locals:\n";
623 for (word l = 0; l < num_locals; l++) {
624 os << " " << l;
625 if (l < var_names_length) {
626 os << ' ' << var_names.at(l);
627 } else if (l < var_names_length + freevar_names_length) {
628 os << ' ' << freevar_names.at(l - var_names_length);
629 } else if (l <
630 var_names_length + freevar_names_length + cellvar_names_length) {
631 os << ' '
632 << cellvar_names.at(l - var_names_length - freevar_names_length);
633 }
634 os << ": " << frame->local(l) << '\n';
635 }
636
637 if (stack_pointer != nullptr) {
638 RawObject* base = reinterpret_cast<RawObject*>(frame);
639 word stack_size = base - stack_pointer;
640 if (stack_size > 0) {
641 os << " stack:\n";
642 for (word i = stack_size - 1; i >= 0; i--) {
643 os << " " << i << ": " << stack_pointer[i] << '\n';
644 }
645 }
646 }
647}
648
649std::ostream& operator<<(std::ostream& os, Frame* frame) {
650 if (frame == nullptr) {
651 return os << "<nullptr>";
652 }
653
654 Vector<Frame*> frames;
655 for (Frame* f = frame; f != nullptr; f = f->previousFrame()) {
656 frames.push_back(f);
657 }
658
659 Thread* thread = Thread::current();
660 for (word i = frames.size() - 1; i >= 0; i--) {
661 RawObject* stack_pointer;
662 if (i == 0) {
663 stack_pointer = thread->stackPointer();
664 } else {
665 stack_pointer = frames[i - 1]->frameEnd();
666 }
667 dumpSingleFrame(thread, os, frames[i], stack_pointer);
668 }
669 return os;
670}
671
672static bool dumpSimple(std::ostream& os, RawObject value) {
673 switch (value.layoutId()) {
674 case LayoutId::kBool:
675 os << Bool::cast(value);
676 return true;
677 case LayoutId::kBoundMethod:
678 os << BoundMethod::cast(value);
679 return true;
680 case LayoutId::kBytearray:
681 os << Bytearray::cast(value);
682 return true;
683 case LayoutId::kCode:
684 os << Code::cast(value);
685 return true;
686 case LayoutId::kDict:
687 os << Dict::cast(value);
688 return true;
689 case LayoutId::kError:
690 os << Error::cast(value);
691 return true;
692 case LayoutId::kFloat:
693 os << Float::cast(value);
694 return true;
695 case LayoutId::kFunction:
696 os << Function::cast(value);
697 return true;
698 case LayoutId::kLargeBytes:
699 os << Bytes::cast(value);
700 return true;
701 case LayoutId::kLargeInt:
702 os << LargeInt::cast(value);
703 return true;
704 case LayoutId::kLargeStr:
705 os << LargeStr::cast(value);
706 return true;
707 case LayoutId::kLayout:
708 os << Layout::cast(value);
709 return true;
710 case LayoutId::kList:
711 os << List::cast(value);
712 return true;
713 case LayoutId::kModule:
714 os << Module::cast(value);
715 return true;
716 case LayoutId::kMutableBytes:
717 os << Bytes::cast(value);
718 return true;
719 case LayoutId::kMutableTuple:
720 os << MutableTuple::cast(value);
721 return true;
722 case LayoutId::kNoneType:
723 os << NoneType::cast(value);
724 return true;
725 case LayoutId::kSmallBytes:
726 os << Bytes::cast(value);
727 return true;
728 case LayoutId::kSmallInt:
729 os << SmallInt::cast(value);
730 return true;
731 case LayoutId::kSmallStr:
732 os << SmallStr::cast(value);
733 return true;
734 case LayoutId::kTuple:
735 os << Tuple::cast(value);
736 return true;
737 case LayoutId::kType:
738 os << Type::cast(value);
739 return true;
740 case LayoutId::kValueCell:
741 os << ValueCell::cast(value);
742 return true;
743 case LayoutId::kWeakLink:
744 os << WeakLink::cast(value);
745 return true;
746 default:
747 return false;
748 }
749}
750
751std::ostream& operator<<(std::ostream& os, Thread* thread) {
752 HandleScope scope(thread);
753 Object type(&scope, thread->pendingExceptionType());
754 os << "pending exception type: " << *type << "\n";
755 Object value(&scope, thread->pendingExceptionValue());
756 os << "pending exception value: " << *value << "\n";
757 Object traceback(&scope, thread->pendingExceptionTraceback());
758 os << "pending exception traceback: " << *traceback << "\n";
759 return os;
760}
761
762std::ostream& operator<<(std::ostream& os, LayoutId layout_id) {
763 os << "layout " << static_cast<word>(layout_id);
764 Thread* thread = Thread::current();
765 HandleScope scope(thread);
766 Runtime* runtime = thread->runtime();
767 Object layout_obj(&scope, runtime->layoutAtSafe(layout_id));
768 if (!layout_obj.isLayout()) {
769 os << '\n';
770 return os;
771 }
772 Layout layout(&scope, *layout_obj);
773 if (!runtime->isInstanceOfType(layout.describedType())) {
774 os << '\n';
775 return os;
776 }
777 Type type(&scope, layout.describedType());
778 os << " (" << type << "):\n";
779 return os;
780}
781
782USED void dump(RawObject object) { dumpExtended(std::cerr, object); }
783
784USED void dump(const Object& object) { dumpExtended(std::cerr, *object); }
785
786USED void dump(Frame* frame) { std::cerr << frame; }
787
788USED void dump(LayoutId id) { std::cerr << id; }
789
790USED void dumpPendingException(Thread* thread) { std::cerr << thread; }
791
792USED void dumpSingleFrame(Frame* frame) {
793 dumpSingleFrame(Thread::current(), std::cerr, frame, nullptr);
794}
795
796void dumpTraceback() {
797 Thread* thread = Thread::current();
798 thread->runtime()->printTraceback(thread, File::kStderr);
799}
800
801void initializeDebugging() {
802 // This function must be called even though it is empty. If it is not called
803 // then there is no reference from another file left and the linker will not
804 // even look at the whole compilation unit and miss the `attribute((used))`
805 // annotations on the dump functions.
806}
807
808} // namespace py