| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #include "backtrace.h" | 
| 2 |  |  |  |  |  |  | #include | 
| 3 |  |  |  |  |  |  | #include | 
| 4 |  |  |  |  |  |  | #include | 
| 5 |  |  |  |  |  |  | #include "PerlTrace.h" | 
| 6 |  |  |  |  |  |  | #include | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | using namespace panda; | 
| 9 |  |  |  |  |  |  | using panda::Backtrace; | 
| 10 |  |  |  |  |  |  | using xs::my_perl; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | #ifndef CvHASGV | 
| 13 |  |  |  |  |  |  | // for perls < 5.22 | 
| 14 |  |  |  |  |  |  | #define CvHASGV(cv) cBOOL(SvANY(cv)->xcv_gv_u.xcv_gv) | 
| 15 |  |  |  |  |  |  | #endif | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | namespace xs { | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | using PerlTraceSP = iptr; | 
| 21 |  |  |  |  |  |  |  | 
| 22 | 106 |  |  |  |  |  | static string stringize_arg(SV* it) { | 
| 23 | 106 |  |  |  |  |  | string value; | 
| 24 | 106 | 50 |  |  |  |  | if (SvIS_FREED(it)) { value = "n/a"; }  // already freed | 
|  |  | 0 |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | else { | 
| 26 | 212 |  |  |  |  |  | Sv sv(it); | 
| 27 | 106 |  |  |  |  |  | bool escape = false; | 
| 28 | 106 | 100 |  |  |  |  | if      (!sv.defined())  { value += "undef"; } | 
|  |  | 50 |  |  |  |  |  | 
| 29 | 104 | 100 |  |  |  |  | else if (sv.is_simple()) { | 
| 30 | 100 | 50 |  |  |  |  | Simple simple(sv); | 
| 31 | 50 | 50 |  |  |  |  | value = simple.as_string(); | 
|  |  | 50 |  |  |  |  |  | 
| 32 | 50 | 50 |  |  |  |  | escape = !simple.is_like_number(); | 
| 33 |  |  |  |  |  |  | } | 
| 34 |  |  |  |  |  |  | else { | 
| 35 |  |  |  |  |  |  | char buff[32]; | 
| 36 | 54 | 50 |  |  |  |  | auto res = to_chars(buff, buff+32, uint64_t(it), 16); | 
| 37 | 54 | 50 |  |  |  |  | if (!res.ec) { | 
| 38 | 54 |  |  |  |  |  | escape = true; | 
| 39 | 54 |  |  |  |  |  | auto size = res.ptr - buff; | 
| 40 | 108 | 50 |  |  |  |  | string addr = string("(0x") + string(buff, static_cast(size)) + ")"; | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 108 |  |  |  |  |  | string type = "UNKNOWN"; | 
| 43 | 54 | 50 |  |  |  |  | if     (sv.is_io_ref())    { type = "IO"; } | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 44 | 54 | 50 |  |  |  |  | else if(sv.is_sub_ref())   { type = "CODE"; } | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 45 | 9 | 50 |  |  |  |  | else if(sv.is_array_ref()) { type = "ARRAY"; } | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 46 | 7 | 50 |  |  |  |  | else if(sv.is_hash_ref())  { type = "HASH"; } | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 47 | 2 | 50 |  |  |  |  | else if(sv.is_stash())     { type = "STASH"; } | 
|  |  | 0 |  |  |  |  |  | 
| 48 | 2 | 50 |  |  |  |  | else if(sv.is_ref())       { type = "SCALAR"; } | 
|  |  | 50 |  |  |  |  |  | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 54 | 100 |  |  |  |  | if(sv.is_object_ref()) { | 
| 51 | 3 | 50 |  |  |  |  | addr = string("=") + type + addr; | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 52 | 6 | 50 |  |  |  |  | Object obj(sv); | 
| 53 | 3 | 50 |  |  |  |  | type = obj.stash().effective_name(); | 
|  |  | 50 |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | } | 
| 55 | 54 | 50 |  |  |  |  | value = type + addr; | 
|  |  | 50 |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | } | 
| 57 | 54 | 0 |  |  |  |  | else { value = "*ERROR*"; } | 
| 58 |  |  |  |  |  |  | } | 
| 59 | 106 | 100 |  |  |  |  | value = (escape ? "'" : "") + value + (escape ? "'" : ""); | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | } | 
| 61 | 106 |  |  |  |  |  | return value; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 139 |  |  |  |  |  | static std::vector get_args(const PERL_CONTEXT* cx) { | 
| 65 | 139 |  |  |  |  |  | std::vector r; | 
| 66 | 139 | 100 |  |  |  |  | if (CxTYPE(cx) == CXt_SUB && CxHASARGS(cx)) { | 
|  |  | 50 |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | /* slot 0 of the pad contains the original @_ */ | 
| 68 | 91 |  |  |  |  |  | AV * const ary = MUTABLE_AV(AvARRAY(MUTABLE_AV(PadlistARRAY(CvPADLIST(cx->blk_sub.cv))[cx->blk_sub.olddepth+1]))[0]); | 
| 69 | 91 | 50 |  |  |  |  | auto args_count = av_top_index(ary); | 
| 70 |  |  |  |  |  |  | //auto off = AvARRAY(ary) - AvALLOC(ary); | 
| 71 | 91 |  |  |  |  |  | auto off = 0; | 
| 72 | 91 |  |  |  |  |  | auto arr = AvARRAY(ary); | 
| 73 | 91 |  |  |  |  |  | auto last = args_count + off; | 
| 74 | 197 | 100 |  |  |  |  | for(decltype(off) i = off; i <= last; ++i) { | 
| 75 | 106 |  |  |  |  |  | auto it = arr[i]; | 
| 76 | 106 | 50 |  |  |  |  | r.emplace_back(stringize_arg(it)); | 
|  |  | 50 |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  | } | 
| 79 | 139 |  |  |  |  |  | return r; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 25 |  |  |  |  |  | static PerlTraceSP get_trace() noexcept { | 
| 83 |  |  |  |  |  |  | dTHX; | 
| 84 | 50 |  |  |  |  |  | std::vector frames; | 
| 85 | 25 |  |  |  |  |  | I32 level = 0; | 
| 86 | 25 |  |  |  |  |  | const PERL_CONTEXT *dbcx = nullptr; | 
| 87 | 25 |  |  |  |  |  | const PERL_CONTEXT* cx = caller_cx(level, &dbcx); | 
| 88 | 164 | 100 |  |  |  |  | while (cx) { | 
| 89 | 139 | 50 |  |  |  |  | auto pv_raw = CopSTASHPV(cx->blk_oldcop); | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 90 | 139 | 50 |  |  |  |  | auto file = CopFILE(cx->blk_oldcop); | 
| 91 | 139 |  |  |  |  |  | auto line = CopLINE(cx->blk_oldcop); | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 278 |  |  |  |  |  | xs::Sub sub; | 
| 94 | 278 |  |  |  |  |  | string name; | 
| 95 | 278 |  |  |  |  |  | string library; | 
| 96 | 139 | 100 |  |  |  |  | if ((CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)) { | 
|  |  | 50 |  |  |  |  |  | 
| 97 | 91 | 50 |  |  |  |  | if (CvHASGV(dbcx->blk_sub.cv)) { | 
| 98 | 182 |  |  |  |  |  | xs::Sub sub(dbcx->blk_sub.cv); | 
| 99 | 91 |  |  |  |  |  | name = sub.name(); | 
| 100 |  |  |  |  |  |  | // just sub.stash().name() can't be called, as it omits | 
| 101 |  |  |  |  |  |  | // the effects of Sub::Name | 
| 102 | 91 |  |  |  |  |  | library = sub.glob().effective_stash().name(); | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | } else { | 
| 105 | 0 |  |  |  |  |  | name = "(unknown)"; | 
| 106 | 91 |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  | } else { | 
| 108 | 48 |  |  |  |  |  | name = "(eval)"; | 
| 109 |  |  |  |  |  |  | } | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 139 | 100 |  |  |  |  | if (!library && pv_raw) { library = pv_raw; }; | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 278 |  |  |  |  |  | auto args = get_args(cx); | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 278 |  |  |  |  |  | StackframeSP frame(new Stackframe()); | 
| 116 | 139 |  |  |  |  |  | frame->library = library; | 
| 117 | 139 |  |  |  |  |  | frame->file = file; | 
| 118 | 139 |  |  |  |  |  | frame->line_no = line; | 
| 119 | 139 |  |  |  |  |  | frame->name = name; | 
| 120 | 139 |  |  |  |  |  | frame->args = std::move(args); | 
| 121 | 139 |  |  |  |  |  | frames.emplace_back(std::move(frame)); | 
| 122 |  |  |  |  |  |  |  | 
| 123 | 139 |  |  |  |  |  | ++level; | 
| 124 | 139 |  |  |  |  |  | cx = caller_cx(level, &dbcx); | 
| 125 |  |  |  |  |  |  | } | 
| 126 | 25 |  |  |  |  |  | return new PerlTrace(std::move(frames)); | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | Sv::payload_marker_t backtrace_c_marker{}; | 
| 132 |  |  |  |  |  |  | Sv::payload_marker_t backtrace_perl_marker{}; | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 17 |  |  |  |  |  | int payload_backtrace_c_free(pTHX_ SV*, MAGIC* mg) { | 
| 135 | 17 | 50 |  |  |  |  | if (mg->mg_virtual == &backtrace_c_marker) { | 
| 136 | 17 |  |  |  |  |  | auto* payload = static_cast((void*)mg->mg_ptr); | 
| 137 | 17 | 50 |  |  |  |  | delete payload; | 
| 138 |  |  |  |  |  |  | } | 
| 139 | 17 |  |  |  |  |  | return 0; | 
| 140 |  |  |  |  |  |  | } | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 13 |  |  |  |  |  | string _get_backtrace_string(Ref except, bool include_c_trace) { | 
| 143 | 13 |  |  |  |  |  | string result; | 
| 144 | 26 |  |  |  |  |  | auto it = except.value(); | 
| 145 | 13 | 100 |  |  |  |  | if (include_c_trace) { | 
| 146 | 24 |  |  |  |  |  | string c_trace; | 
| 147 | 12 | 100 |  |  |  |  | if (it.payload_exists(&backtrace_c_marker)) { | 
| 148 | 11 |  |  |  |  |  | auto payload = it.payload(&backtrace_c_marker); | 
| 149 | 11 |  |  |  |  |  | auto bt = static_cast(payload.ptr); | 
| 150 | 22 |  |  |  |  |  | auto bt_info = bt->get_backtrace_info(); | 
| 151 | 11 | 50 |  |  |  |  | if (bt_info) { | 
| 152 | 11 | 50 |  |  |  |  | c_trace += "C backtrace:\n"; | 
| 153 | 11 | 50 |  |  |  |  | c_trace += bt_info->to_string(); | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  | } | 
| 156 | 12 | 100 |  |  |  |  | if (!c_trace) { result = "\n"; } | 
|  |  | 50 |  |  |  |  |  | 
| 157 | 11 | 50 |  |  |  |  | else          { result = c_trace;                  } | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 | 13 | 100 |  |  |  |  | if (it.payload_exists(&backtrace_perl_marker)) { | 
| 161 | 12 | 50 |  |  |  |  | result += "Perl backtrace:\n"; | 
| 162 | 12 |  |  |  |  |  | auto payload = it.payload(&backtrace_perl_marker); | 
| 163 | 12 | 50 |  |  |  |  | auto bt = xs::in(payload.obj); | 
| 164 | 83 | 100 |  |  |  |  | for (const auto& frame : bt->get_frames() ) { | 
| 165 | 71 | 50 |  |  |  |  | result += frame->library + "::" + frame->name + " at " + frame->file + ":" + string::from_number(frame->line_no, 10) + "\n"; | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  | else { | 
| 169 | 1 | 50 |  |  |  |  | result += ""; | 
| 170 |  |  |  |  |  |  | } | 
| 171 | 26 |  |  |  |  |  | return result; | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 | 12 | 50 |  |  |  |  | string get_backtrace_string(Ref except) { return _get_backtrace_string(except, true); } | 
| 175 | 1 | 50 |  |  |  |  | string get_backtrace_string_pp(Ref except) { return _get_backtrace_string(except, false); } | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 2 |  |  |  |  |  | panda::iptr get_backtrace(Ref except) { | 
| 178 | 2 |  |  |  |  |  | panda::iptr r; | 
| 179 | 4 |  |  |  |  |  | auto it = except.value(); | 
| 180 | 2 | 50 |  |  |  |  | if (it.payload_exists(&backtrace_perl_marker)) { | 
| 181 | 2 | 50 |  |  |  |  | r = new DualTrace(); | 
|  |  | 50 |  |  |  |  |  | 
| 182 | 2 |  |  |  |  |  | auto payload = it.payload(&backtrace_perl_marker); | 
| 183 | 2 | 50 |  |  |  |  | auto bt = xs::in(payload.obj); | 
| 184 | 2 |  |  |  |  |  | r->perl_trace = bt; | 
| 185 |  |  |  |  |  |  | } | 
| 186 | 2 | 50 |  |  |  |  | if (r && it.payload_exists(&backtrace_c_marker)) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 187 | 2 |  |  |  |  |  | auto payload = it.payload(&backtrace_c_marker); | 
| 188 | 2 |  |  |  |  |  | auto bt = static_cast(payload.ptr); | 
| 189 | 2 |  |  |  |  |  | r->c_trace = bt->get_backtrace_info(); | 
| 190 |  |  |  |  |  |  | } | 
| 191 | 4 |  |  |  |  |  | return r; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 1 |  |  |  |  |  | panda::iptr create_backtrace() { | 
| 195 | 1 | 50 |  |  |  |  | panda::iptr r(new DualTrace()); | 
|  |  | 50 |  |  |  |  |  | 
| 196 | 2 |  |  |  |  |  | Backtrace bt; | 
| 197 | 1 |  |  |  |  |  | r->c_trace = bt.get_backtrace_info(); | 
| 198 | 1 |  |  |  |  |  | r->perl_trace = get_trace(); | 
| 199 | 2 |  |  |  |  |  | return r; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 26 |  |  |  |  |  | Ref _is_safe_to_wrap(Sv& ex, bool add_frame_info) { | 
| 203 | 26 |  |  |  |  |  | Ref ref; | 
| 204 | 26 | 100 |  |  |  |  | if (!ex.is_ref()) { | 
| 205 |  |  |  |  |  |  | /* try to mimic perl string error, i.e. "my-error at t/06-c-exceptions.t line 10." | 
| 206 |  |  |  |  |  |  | * we need that as when an exception is thrown from C-code, we wrap it into object | 
| 207 |  |  |  |  |  |  | * and frame info isn't addeded by Perl. | 
| 208 |  |  |  |  |  |  | * | 
| 209 |  |  |  |  |  |  | * When an exception is thrown from Perl, Perl already added frame info. | 
| 210 |  |  |  |  |  |  | */ | 
| 211 | 13 | 100 |  |  |  |  | if (add_frame_info && ex.is_simple()) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 8 | 50 |  |  |  |  | auto str = Simple(ex).as_string(); | 
|  |  | 50 |  |  |  |  |  | 
| 214 | 4 | 50 |  |  |  |  | bool ends_with_newline = str.size() && str[str.size() - 1] == '\n'; | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 215 | 4 | 100 |  |  |  |  | if (!ends_with_newline) { | 
| 216 | 3 | 50 |  |  |  |  | auto messed = Perl_mess_sv(aTHX_ ex, false); | 
| 217 | 3 | 50 |  |  |  |  | ref = Stash("Exception::Backtrace").call("new", Simple(messed)); | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  | } | 
| 220 | 13 | 100 |  |  |  |  | if (!ref) { | 
| 221 | 13 | 50 |  |  |  |  | ref = Stash("Exception::Backtrace").call("new", ex); | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  | else { | 
| 225 | 26 | 50 |  |  |  |  | Ref tmp_ref(ex); | 
| 226 | 26 |  |  |  |  |  | auto it = tmp_ref.value(); | 
| 227 | 13 | 50 |  |  |  |  | if (!(it.is_scalar() && it.readonly())) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 228 | 12 | 50 |  |  |  |  | ref = tmp_ref; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  | } | 
| 231 | 26 |  |  |  |  |  | return ref; | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | }; | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 19 |  |  |  |  |  | static void attach_backtraces(Ref except, const PerlTraceSP& perl_trace) { | 
| 236 | 38 |  |  |  |  |  | auto it = except.value(); | 
| 237 | 19 | 100 |  |  |  |  | if (!it.payload_exists(&backtrace_c_marker)) { | 
| 238 | 13 | 50 |  |  |  |  | auto bt = new Backtrace(); | 
| 239 | 13 | 50 |  |  |  |  | it.payload_attach(bt, &backtrace_c_marker); | 
| 240 |  |  |  |  |  |  | } | 
| 241 | 19 | 100 |  |  |  |  | if (!it.payload_exists(&backtrace_perl_marker)) { | 
| 242 | 13 | 50 |  |  |  |  | it.payload_attach(xs::out(perl_trace.get()), &backtrace_perl_marker); | 
|  |  | 50 |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | } | 
| 244 | 19 |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 21 |  |  |  |  |  | Sv safe_wrap_exception(Sv ex) { | 
| 247 | 42 | 50 |  |  |  |  | auto ref = _is_safe_to_wrap(ex, false); | 
| 248 | 21 | 100 |  |  |  |  | if (ref) { | 
| 249 | 40 |  |  |  |  |  | auto perl_traces = get_trace(); | 
| 250 | 20 |  |  |  |  |  | auto& frames = perl_traces->get_frames(); | 
| 251 | 238 | 50 |  |  |  |  | bool in_destroy = std::any_of(frames.begin(), frames.end(), [](auto& frame) { return frame->name == "DESTROY"; } ); | 
| 252 | 20 | 100 |  |  |  |  | if (in_destroy) { | 
| 253 |  |  |  |  |  |  | // we don't want to corrupt Perl's warning with Exception::Backtrace handler, instead let it warns | 
| 254 |  |  |  |  |  |  | // to the origin of the exception | 
| 255 | 1 | 50 |  |  |  |  | return Simple::undef; | 
| 256 |  |  |  |  |  |  | } | 
| 257 | 19 | 50 |  |  |  |  | attach_backtraces(ref, perl_traces); | 
|  |  | 50 |  |  |  |  |  | 
| 258 | 19 | 50 |  |  |  |  | return Sv(ref); | 
| 259 |  |  |  |  |  |  | } | 
| 260 | 1 | 50 |  |  |  |  | return Simple::undef; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  |  | 
| 264 | 6 |  |  |  |  |  | void install_exception_processor() { | 
| 265 | 17 | 50 |  |  |  |  | add_exception_processor([](Sv& ex) -> Sv { | 
| 266 | 14 | 50 |  |  |  |  | auto ref = _is_safe_to_wrap(ex, true); | 
|  |  | 100 |  |  |  |  |  | 
| 267 | 5 | 50 |  |  |  |  | if (ref) { | 
| 268 | 10 |  |  |  |  |  | auto it = ref.value(); | 
| 269 | 5 | 100 |  |  |  |  | if (!it.payload_exists(&backtrace_c_marker)) { | 
| 270 | 8 |  |  |  |  |  | try { throw; } | 
| 271 | 4 | 50 |  |  |  |  | catch (const panda::Backtrace& err) { | 
| 272 |  |  |  |  |  |  | // reuse existing c trace | 
| 273 | 2 | 50 |  |  |  |  | it.payload_attach(new Backtrace(err), &backtrace_c_marker); | 
|  |  | 50 |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | } | 
| 275 | 4 | 50 |  |  |  |  | catch (...) { | 
| 276 |  |  |  |  |  |  | // add new c trace | 
| 277 | 2 | 50 |  |  |  |  | it.payload_attach(new Backtrace(), &backtrace_c_marker); | 
|  |  | 50 |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  | } | 
| 280 | 5 | 100 |  |  |  |  | if (!it.payload_exists(&backtrace_perl_marker)) { | 
| 281 | 8 |  |  |  |  |  | auto bt = get_trace(); | 
| 282 | 4 | 50 |  |  |  |  | it.payload_attach(xs::out(bt), &backtrace_perl_marker); | 
|  |  | 50 |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | } | 
| 284 | 5 | 50 |  |  |  |  | return Sv(ref); | 
| 285 |  |  |  |  |  |  | } | 
| 286 | 0 | 0 |  |  |  |  | return ex; | 
| 287 | 6 | 50 |  |  |  |  | }); | 
| 288 | 6 |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  |  | 
| 290 | 24 | 50 |  |  |  |  | } | 
|  |  | 50 |  |  |  |  |  |