this repo has no description
at trunk 311 lines 11 kB view raw
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