xref: /aosp_15_r20/external/pytorch/torch/csrc/profiler/combined_traceback.cpp (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1 #include <torch/csrc/profiler/combined_traceback.h>
2 #include <torch/csrc/utils/cpp_stacktraces.h>
3 
4 namespace torch {
5 
6 static std::atomic<CapturedTraceback::Python*> python_support_ = nullptr;
7 
gather(bool python,bool script,bool cpp)8 std::shared_ptr<CapturedTraceback> CapturedTraceback::gather(
9     bool python,
10     bool script,
11     bool cpp) {
12   auto r = std::make_shared<CapturedTraceback>();
13   if (python) {
14     auto p = python_support_.load();
15     while (p && r->frames_.empty()) {
16       r->frames_ = p->gather();
17       r->python_ = p;
18       p = p->next_;
19     }
20   }
21   if (script) {
22     r->script_frames_ = torch::jit::currentCallstack();
23   }
24   if (cpp) {
25     r->cpp_frames_ = unwind::unwind();
26   }
27   return r;
28 }
29 
traversePython(visitproc visit,void * arg)30 int CapturedTraceback::traversePython(visitproc visit, void* arg) {
31   TORCH_INTERNAL_ASSERT(python_);
32   return python_->traverse(frames_, visit, arg);
33 }
34 
clearPython()35 int CapturedTraceback::clearPython() {
36   TORCH_INTERNAL_ASSERT(python_);
37   return python_->clear(frames_);
38 }
39 
~CapturedTraceback()40 CapturedTraceback::~CapturedTraceback() {
41   if (!frames_.empty()) {
42     TORCH_INTERNAL_ASSERT(python_);
43     python_->release(frames_);
44   }
45 }
46 
47 struct PyFrameHash {
operator ()torch::PyFrameHash48   std::size_t operator()(const CapturedTraceback::PyFrame& f) const {
49     return std::hash<void*>()(f.code) ^ std::hash<int>()(f.lasti);
50   }
51 };
52 
53 struct PyFrameEq {
operator ()torch::PyFrameEq54   std::size_t operator()(
55       const CapturedTraceback::PyFrame& lhs,
56       const CapturedTraceback::PyFrame& rhs) const {
57     return lhs.code == rhs.code && lhs.lasti == rhs.lasti;
58   }
59 };
60 
symbolize(const std::vector<CapturedTraceback * > & to_symbolize)61 SymbolizedTracebacks symbolize(
62     const std::vector<CapturedTraceback*>& to_symbolize) {
63   SymbolizedTracebacks r;
64 
65   std::unordered_map<void*, size_t> ip_to_frame_offset;
66   std::unordered_map<CapturedTraceback::PyFrame, size_t, PyFrameHash, PyFrameEq>
67       py_to_frame_offset;
68   std::vector<void*> all_cpp_ips;
69 
70   // dedup and collect any C++ frames that need symbols for
71   for (const auto& e : to_symbolize) {
72     for (void* f : e->cpp_frames_) {
73       if (!ip_to_frame_offset.count(f)) {
74         ip_to_frame_offset[f] = all_cpp_ips.size();
75         all_cpp_ips.push_back(f);
76       }
77     }
78   }
79   // gather symbol names for C++ frames
80   if (!all_cpp_ips.empty()) {
81     r.all_frames = unwind::symbolize(all_cpp_ips, torch::get_symbolize_mode());
82   }
83 
84   // batch symbolization requests so we dedup frame objects
85   // however, we might have to request from different python interpreters
86   // make sure we flush requests before switching interpreters;
87   CapturedTraceback::Python* cur_python = nullptr;
88   std::vector<CapturedTraceback::PyFrame> cur_py_frames;
89   size_t py_frames_size_ = 0;
90 
91   for (const auto& e : to_symbolize) {
92     if (e->python_) {
93       if (cur_python != e->python_ && !cur_py_frames.empty()) {
94         if (cur_python) {
95           // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
96           cur_python->appendSymbolized(cur_py_frames, r);
97         }
98         cur_py_frames.clear();
99       }
100       cur_python = e->python_;
101       for (const auto& f : e->frames_) {
102         if (!py_to_frame_offset.count(f)) {
103           py_to_frame_offset[f] = py_frames_size_++;
104           cur_py_frames.push_back(f);
105         }
106       }
107     }
108   }
109   if (!cur_py_frames.empty()) {
110     if (cur_python) {
111       // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
112       cur_python->appendSymbolized(cur_py_frames, r);
113     }
114     cur_py_frames.clear();
115   }
116   std::vector<std::vector<uint64_t>> python_frame_fragments =
117       std::move(r.tracebacks);
118   r.tracebacks = {};
119 
120   for (const auto& sc : to_symbolize) {
121     r.tracebacks.emplace_back();
122     auto py_it = sc->frames_.begin();
123     auto py_end = sc->frames_.end();
124 
125     bool jit_appended = false;
126 
127     auto append_python = [&](const CapturedTraceback::PyFrame& f) {
128       const auto& fragment =
129           python_frame_fragments.at(py_to_frame_offset.at(f));
130       r.tracebacks.back().insert(
131           r.tracebacks.back().end(), fragment.begin(), fragment.end());
132     };
133 
134     auto append_jit = [&]() {
135       if (jit_appended) {
136         return;
137       }
138       jit_appended = true;
139       for (const auto& f : sc->script_frames_) {
140         unwind::Frame frame;
141         frame.funcname =
142             f.filename; // sic: torchscript puts funcname in filename field
143         auto flc = f.range.file_line_col();
144         if (flc) {
145           size_t col = 0;
146           std::tie(frame.filename, frame.lineno, col) = *flc;
147         } else {
148           frame.filename = "??";
149           frame.lineno = 0;
150         }
151         r.tracebacks.back().push_back(r.all_frames.size());
152         r.all_frames.emplace_back(std::move(frame));
153       }
154     };
155 
156     for (void* f : sc->cpp_frames_) {
157       uint64_t cpp_frame = ip_to_frame_offset.at(f);
158       const unwind::Frame& uf = r.all_frames.at(cpp_frame);
159       if (uf.funcname.find("PyEval_EvalFrame") != std::string::npos) {
160         if (py_it != py_end) {
161           append_python(*py_it++);
162         }
163       } else if (
164           uf.funcname.rfind("torch::jit::InterpreterStateImpl::run", 0) !=
165           std::string::npos) {
166         append_jit();
167       }
168       r.tracebacks.back().push_back(cpp_frame);
169     }
170 
171     // add frames if we otherwise haven't seen the C++ frame indicating where
172     // it should go
173     append_jit();
174 
175     for (; py_it != py_end; ++py_it) {
176       append_python(*py_it);
177     }
178   }
179   return r;
180 }
181 
addPythonUnwinder(CapturedTraceback::Python * p)182 void CapturedTraceback::addPythonUnwinder(CapturedTraceback::Python* p) {
183   CapturedTraceback::Python* old_unwinder = python_support_.load();
184   do {
185     p->next_ = old_unwinder;
186   } while (!python_support_.compare_exchange_strong(old_unwinder, p));
187 }
188 
189 } // namespace torch
190