|  | #include <torch/csrc/profiler/combined_traceback.h> | 
|  |  | 
|  | namespace torch { | 
|  |  | 
|  | static std::atomic<CapturedTraceback::Python*> python_support_ = nullptr; | 
|  |  | 
|  | std::shared_ptr<CapturedTraceback> CapturedTraceback::gather( | 
|  | bool python, | 
|  | bool script, | 
|  | bool cpp) { | 
|  | auto r = std::make_shared<CapturedTraceback>(); | 
|  | if (python) { | 
|  | auto p = python_support_.load(); | 
|  | while (p && r->frames_.size() == 0) { | 
|  | r->frames_ = p->gather(); | 
|  | r->python_ = p; | 
|  | p = p->next_; | 
|  | } | 
|  | } | 
|  | if (script) { | 
|  | r->script_frames_ = torch::jit::currentCallstack(); | 
|  | } | 
|  | if (cpp) { | 
|  | r->cpp_frames_ = unwind::unwind(); | 
|  | } | 
|  | return r; | 
|  | } | 
|  |  | 
|  | CapturedTraceback::~CapturedTraceback() { | 
|  | if (frames_.size() > 0) { | 
|  | TORCH_INTERNAL_ASSERT(python_); | 
|  | python_->release(frames_); | 
|  | } | 
|  | } | 
|  |  | 
|  | struct PyFrameHash { | 
|  | std::size_t operator()(const CapturedTraceback::PyFrame& f) const { | 
|  | return std::hash<void*>()(f.code) ^ std::hash<int>()(f.lasti); | 
|  | } | 
|  | }; | 
|  |  | 
|  | struct PyFrameEq { | 
|  | std::size_t operator()( | 
|  | const CapturedTraceback::PyFrame& lhs, | 
|  | const CapturedTraceback::PyFrame& rhs) const { | 
|  | return lhs.code == rhs.code && lhs.lasti == rhs.lasti; | 
|  | } | 
|  | }; | 
|  |  | 
|  | SymbolizedTracebacks symbolize( | 
|  | const std::vector<CapturedTraceback*>& to_symbolize) { | 
|  | SymbolizedTracebacks r; | 
|  |  | 
|  | std::unordered_map<void*, size_t> ip_to_frame_offset; | 
|  | std::unordered_map<CapturedTraceback::PyFrame, size_t, PyFrameHash, PyFrameEq> | 
|  | py_to_frame_offset; | 
|  | std::vector<void*> all_cpp_ips; | 
|  |  | 
|  | // dedup and collect any C++ frames that need symbols for | 
|  | for (const auto& e : to_symbolize) { | 
|  | for (void* f : e->cpp_frames_) { | 
|  | if (!ip_to_frame_offset.count(f)) { | 
|  | ip_to_frame_offset[f] = all_cpp_ips.size(); | 
|  | all_cpp_ips.push_back(f); | 
|  | } | 
|  | } | 
|  | } | 
|  | // gather symbol names for C++ frames | 
|  | if (all_cpp_ips.size() > 0) { | 
|  | r.all_frames = unwind::symbolize(all_cpp_ips); | 
|  | } | 
|  |  | 
|  | // batch symbolization requests so we dedup frame objects | 
|  | // however, we might have to request from different python interpreters | 
|  | // make sure we flush requests before switching interpreters; | 
|  | CapturedTraceback::Python* cur_python = nullptr; | 
|  | std::vector<CapturedTraceback::PyFrame> cur_py_frames; | 
|  | size_t py_frames_size_ = 0; | 
|  |  | 
|  | for (const auto& e : to_symbolize) { | 
|  | if (e->python_) { | 
|  | if (cur_python != e->python_ && cur_py_frames.size() > 0) { | 
|  | cur_python->appendSymbolized(cur_py_frames, r); | 
|  | cur_py_frames.clear(); | 
|  | } | 
|  | cur_python = e->python_; | 
|  | for (const auto& f : e->frames_) { | 
|  | if (!py_to_frame_offset.count(f)) { | 
|  | py_to_frame_offset[f] = py_frames_size_++; | 
|  | cur_py_frames.push_back(f); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (cur_py_frames.size() > 0) { | 
|  | cur_python->appendSymbolized(cur_py_frames, r); | 
|  | cur_py_frames.clear(); | 
|  | } | 
|  | std::vector<std::vector<uint64_t>> python_frame_fragments = | 
|  | std::move(r.tracebacks); | 
|  |  | 
|  | for (const auto& sc : to_symbolize) { | 
|  | r.tracebacks.emplace_back(); | 
|  | auto py_it = sc->frames_.begin(); | 
|  | auto py_end = sc->frames_.end(); | 
|  |  | 
|  | bool jit_appended = false; | 
|  |  | 
|  | auto append_python = [&](const CapturedTraceback::PyFrame& f) { | 
|  | const auto& fragment = | 
|  | python_frame_fragments.at(py_to_frame_offset.at(f)); | 
|  | r.tracebacks.back().insert( | 
|  | r.tracebacks.back().end(), fragment.begin(), fragment.end()); | 
|  | }; | 
|  |  | 
|  | auto append_jit = [&]() { | 
|  | if (jit_appended) { | 
|  | return; | 
|  | } | 
|  | jit_appended = true; | 
|  | for (const auto& f : sc->script_frames_) { | 
|  | unwind::Frame frame; | 
|  | frame.funcname = | 
|  | f.filename; // sic: torchscript puts funcname in filename field | 
|  | auto flc = f.range.file_line_col(); | 
|  | if (flc) { | 
|  | size_t col; | 
|  | std::tie(frame.filename, frame.lineno, col) = *flc; | 
|  | } else { | 
|  | frame.filename = "??"; | 
|  | frame.lineno = 0; | 
|  | } | 
|  | r.tracebacks.back().push_back(r.all_frames.size()); | 
|  | r.all_frames.emplace_back(std::move(frame)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | for (void* f : sc->cpp_frames_) { | 
|  | uint64_t cpp_frame = ip_to_frame_offset.at(f); | 
|  | const unwind::Frame& uf = r.all_frames.at(cpp_frame); | 
|  | if (uf.funcname.find("PyEval_EvalFrame") != std::string::npos) { | 
|  | if (py_it != py_end) { | 
|  | append_python(*py_it++); | 
|  | } | 
|  | } else if ( | 
|  | uf.funcname.rfind("torch::jit::InterpreterStateImpl::run", 0) != | 
|  | std::string::npos) { | 
|  | append_jit(); | 
|  | } | 
|  | r.tracebacks.back().push_back(cpp_frame); | 
|  | } | 
|  |  | 
|  | // add frames if we otherwise haven't seen the C++ frame indicating where | 
|  | // it should go | 
|  | append_jit(); | 
|  |  | 
|  | for (; py_it != py_end; ++py_it) { | 
|  | append_python(*py_it); | 
|  | } | 
|  | } | 
|  | return r; | 
|  | } | 
|  |  | 
|  | void CapturedTraceback::addPythonUnwinder(CapturedTraceback::Python* p) { | 
|  | CapturedTraceback::Python* old_unwinder = python_support_.load(); | 
|  | do { | 
|  | p->next_ = old_unwinder; | 
|  | } while (!python_support_.compare_exchange_strong(old_unwinder, p)); | 
|  | } | 
|  |  | 
|  | } // namespace torch |