this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "traceback-builtins.h"
3
4#include "builtins.h"
5#include "globals.h"
6#include "os.h"
7#include "str-builtins.h"
8#include "type-builtins.h"
9
10namespace py {
11
12static const BuiltinAttribute kTracebackAttributes[] = {
13 {ID(_traceback__next), RawTraceback::kNextOffset, AttributeFlags::kHidden},
14 {ID(_traceback__function), RawTraceback::kFunctionOffset,
15 AttributeFlags::kHidden},
16 {ID(tb_lasti), RawTraceback::kLastiOffset, AttributeFlags::kReadOnly},
17 {ID(_traceback__lineno), RawTraceback::kLinenoOffset,
18 AttributeFlags::kHidden},
19};
20
21static const word kTracebackLimit = 1000; // PyTraceback_LIMIT
22static const word kTracebackRecursiveCutoff = 3; // TB_RECURSIVE_CUTOFF
23
24void initializeTracebackType(Thread* thread) {
25 addBuiltinType(thread, ID(traceback), LayoutId::kTraceback,
26 /*superclass_id=*/LayoutId::kObject, kTracebackAttributes,
27 Traceback::kSize, /*basetype=*/false);
28}
29
30static RawObject lineRepeated(Thread* thread, word count) {
31 count -= kTracebackRecursiveCutoff;
32 if (count == 1) {
33 return thread->runtime()->newStrFromCStr(
34 " [Previous line repeated 1 more time]\n");
35 }
36 return thread->runtime()->newStrFromFmt(
37 " [Previous line repeated %w more times]\n", count);
38}
39
40static RawObject sourceLine(Thread* thread, const Object& filename,
41 const Object& lineno_obj) {
42 if (!filename.isStr() || !lineno_obj.isSmallInt()) {
43 return NoneType::object();
44 }
45
46 HandleScope scope(thread);
47 Runtime* runtime = thread->runtime();
48 Object linecache(&scope, runtime->symbols()->at(ID(linecache)));
49 if (runtime->findModule(linecache).isErrorNotFound()) {
50 linecache =
51 thread->invokeFunction1(ID(builtins), ID(__import__), linecache);
52 if (linecache.isErrorException()) {
53 thread->clearPendingException();
54 return NoneType::object();
55 }
56 }
57 Object line_obj(&scope, thread->invokeFunction2(ID(linecache), ID(getline),
58 filename, lineno_obj));
59 if (line_obj.isErrorException()) {
60 return *line_obj;
61 }
62
63 CHECK(line_obj.isStr(), "got a non-str line");
64 Str line(&scope, *line_obj);
65 line = strStripSpace(thread, line);
66 word length = line.length();
67 if (length == 0) {
68 return NoneType::object();
69 }
70
71 MutableBytes result(&scope,
72 runtime->newMutableBytesUninitialized(length + 5));
73 result.replaceFromWithByte(0, ' ', 4);
74 result.replaceFromWithStr(4, *line, length);
75 result.byteAtPut(length + 4, '\n');
76 return result.becomeStr();
77}
78
79static RawObject tracebackFilename(Thread* thread, const Traceback& traceback) {
80 HandleScope scope(thread);
81 Object code(&scope, Function::cast(traceback.function()).code());
82 if (!code.isCode()) {
83 return NoneType::object();
84 }
85 Object name(&scope, Code::cast(*code).filename());
86 if (thread->runtime()->isInstanceOfStr(*name)) {
87 return strUnderlying(*name);
88 }
89 return NoneType::object();
90}
91
92static RawObject tracebackFunctionName(Thread* thread,
93 const Traceback& traceback) {
94 HandleScope scope(thread);
95 Function function(&scope, traceback.function());
96 Object name(&scope, function.name());
97 Runtime* runtime = thread->runtime();
98 if (runtime->isInstanceOfStr(*name)) {
99 return strUnderlying(*name);
100 }
101 Object code_obj(&scope, function.code());
102 if (!code_obj.isCode()) {
103 return NoneType::object();
104 }
105 Code code(&scope, *code_obj);
106 if (!code.isNative()) {
107 return NoneType::object();
108 }
109
110 void* addr = Int::cast(code.code()).asCPtr();
111 char name_buf[128];
112 word name_size = std::strlen(name_buf);
113 word name_len = OS::sharedObjectSymbolName(addr, name_buf, name_size);
114
115 if (name_len < 0) {
116 return runtime->newStrFromFmt("<native function at %p (no symbol found)>",
117 addr);
118 }
119 if (name_len < name_size) {
120 return runtime->newStrFromFmt("<native function at %p (%s)>", addr,
121 name_buf);
122 }
123 unique_c_ptr<char> heap_buf(
124 reinterpret_cast<char*>(std::malloc(name_len + 1)));
125 word new_len = OS::sharedObjectSymbolName(addr, heap_buf.get(), name_len + 1);
126 DCHECK(name_len == new_len, "unexpected number of bytes written");
127 return runtime->newStrFromFmt("<native function at %p (%s)>", addr,
128 heap_buf.get());
129}
130
131static RawObject tracebackLineno(Thread* thread, const Traceback& traceback) {
132 HandleScope scope(thread);
133 Object lineno(&scope, traceback.lineno());
134 if (lineno.isSmallInt()) {
135 return *lineno;
136 }
137 Object code_obj(&scope, Function::cast(traceback.function()).code());
138 if (!code_obj.isCode()) {
139 return NoneType::object();
140 }
141 Code code(&scope, *code_obj);
142 if (code.isNative() || !code.lnotab().isBytes()) {
143 return NoneType::object();
144 }
145 word lasti = SmallInt::cast(traceback.lasti()).value();
146 Object result(&scope, SmallInt::fromWord(code.offsetToLineNum(lasti)));
147 traceback.setLineno(*result);
148 return *result;
149}
150
151static void writeCStr(const MutableBytes& dst, word* index, const char* src) {
152 word length = std::strlen(src);
153 dst.replaceFromWithAll(*index, {reinterpret_cast<const byte*>(src), length});
154 *index += length;
155}
156
157static void writeStr(const MutableBytes& dst, word* index, RawStr src) {
158 word length = src.length();
159 dst.replaceFromWithStr(*index, src, length);
160 *index += length;
161}
162
163static word tracebackWriteLine(const Object& filename, const Object& lineno,
164 const Object& function_name,
165 const MutableBytes& dst, bool determine_size) {
166 word index = 0;
167
168 if (filename.isStr()) {
169 if (determine_size) {
170 index += std::strlen(" File \"") + Str::cast(*filename).length() + 1;
171 } else {
172 writeCStr(dst, &index, " File \"");
173 writeStr(dst, &index, Str::cast(*filename));
174 writeCStr(dst, &index, "\"");
175 }
176 } else if (determine_size) {
177 index += std::strlen(" File \"<unknown>\"");
178 } else {
179 writeCStr(dst, &index, " File \"<unknown>\"");
180 }
181
182 if (lineno.isSmallInt()) {
183 char buf[kUwordDigits10 + 9];
184 word size = std::snprintf(buf, sizeof(buf), ", line %ld",
185 SmallInt::cast(*lineno).value());
186 DCHECK_BOUND(size, kUwordDigits10 + 8);
187 if (determine_size) {
188 index += size;
189 } else {
190 writeCStr(dst, &index, buf);
191 }
192 }
193
194 if (function_name.isStr()) {
195 if (determine_size) {
196 index += std::strlen(", in ") + Str::cast(*function_name).length() + 1;
197 } else {
198 writeCStr(dst, &index, ", in ");
199 writeStr(dst, &index, Str::cast(*function_name));
200 writeCStr(dst, &index, "\n");
201 }
202 } else if (determine_size) {
203 index += std::strlen(", in <invalid name>\n");
204 } else {
205 writeCStr(dst, &index, ", in <invalid name>\n");
206 }
207
208 return index;
209}
210
211RawObject tracebackWrite(Thread* thread, const Traceback& traceback,
212 const Object& file) {
213 HandleScope scope(thread);
214 Runtime* runtime = thread->runtime();
215 Object limit_obj(
216 &scope, runtime->lookupNameInModule(thread, ID(sys), ID(tracebacklimit)));
217
218 word limit = kTracebackLimit;
219 if (!limit_obj.isErrorNotFound() && runtime->isInstanceOfInt(*limit_obj)) {
220 limit = intUnderlying(*limit_obj).asWordSaturated();
221 if (limit <= 0) {
222 return NoneType::object();
223 }
224 }
225
226 Str line(&scope,
227 runtime->newStrFromCStr("Traceback (most recent call last):\n"));
228 Object result(&scope, thread->invokeMethod2(file, ID(write), line));
229 if (result.isErrorException()) return *result;
230
231 word depth = 0;
232 Object tb(&scope, *traceback);
233 while (tb.isTraceback()) {
234 depth++;
235 tb = Traceback::cast(*tb).next();
236 }
237
238 Traceback current(&scope, *traceback);
239 while (depth > limit) {
240 depth--;
241 current = current.next();
242 }
243
244 MutableBytes buffer(&scope, runtime->emptyMutableBytes());
245 Object filename(&scope, NoneType::object());
246 Object function_name(&scope, NoneType::object());
247 Object lineno(&scope, NoneType::object());
248 Object last_filename(&scope, NoneType::object());
249 Object last_function_name(&scope, NoneType::object());
250 Object last_lineno(&scope, NoneType::object());
251 Object next(&scope, NoneType::object());
252 for (word count = 0;;) {
253 filename = tracebackFilename(thread, current);
254 lineno = tracebackLineno(thread, current);
255 function_name = tracebackFunctionName(thread, current);
256 bool filename_changed =
257 last_filename.isNoneType() || filename.isNoneType() ||
258 !Str::cast(*last_filename).equals(Str::cast(*filename));
259 bool lineno_changed = last_lineno.isNoneType() || lineno != last_lineno;
260 bool function_name_changed =
261 last_function_name.isNoneType() || function_name.isNoneType() ||
262 !Str::cast(*last_function_name).equals(Str::cast(*function_name));
263 if (filename_changed || lineno_changed || function_name_changed) {
264 if (count > kTracebackRecursiveCutoff) {
265 line = lineRepeated(thread, count);
266 result = thread->invokeMethod2(file, ID(write), line);
267 if (result.isErrorException()) return *result;
268 }
269 last_filename = *filename;
270 last_lineno = *lineno;
271 last_function_name = *function_name;
272 count = 0;
273 }
274
275 count++;
276 if (count <= kTracebackRecursiveCutoff) {
277 word size =
278 tracebackWriteLine(filename, lineno, function_name, buffer, true);
279 buffer = runtime->newMutableBytesUninitialized(size);
280 tracebackWriteLine(filename, lineno, function_name, buffer, false);
281 line = buffer.becomeStr();
282 result = thread->invokeMethod2(file, ID(write), line);
283 if (result.isErrorException()) return *result;
284
285 result = sourceLine(thread, filename, lineno);
286 if (result.isErrorException()) return *result;
287 if (result.isStr()) {
288 result = thread->invokeMethod2(file, ID(write), result);
289 if (result.isErrorException()) return *result;
290 }
291
292 result = runtime->handlePendingSignals(thread);
293 if (result.isErrorException()) return *result;
294 }
295
296 next = current.next();
297 if (next.isNoneType()) {
298 if (count > kTracebackRecursiveCutoff) {
299 line = lineRepeated(thread, count);
300 result = thread->invokeMethod2(file, ID(write), line);
301 if (result.isErrorException()) return *result;
302 }
303 return NoneType::object();
304 }
305
306 current = *next;
307 buffer = runtime->emptyMutableBytes();
308 }
309}
310
311} // namespace py