def asio_stacktrace(wh, limit=None): """Produce a list of async frames by following a WaitHandle's parent chain. The stacktrace ends at the WaitHandle::join(). The whole chain is walked even if `limit' is provided---the return stacktrace will have `limit' or fewer entries if there were `limit' or fewer frames, and `limit' + 1 frames otherwise, where the last frame is the WaitHandle::join(). """ stacktrace = [] count = 0 for wh in WaitHandle(wh).chain(): resumable = wh.resumable() if resumable is None: continue if limit is None or count < limit: stacktrace.append(frame.create_resumable(count, resumable)) count += 1 ar = asio_context(wh['m_contextIdx'])['m_savedFP'] if ar != nullptr(): stacktrace.append(frame.create_php(idx=count, ar=ar)) return stacktrace
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() try: arch = gdb.newest_frame().architecture().name() except: arch = 'i386:x86-64' frame_reg = '$x29' if arch == 'aarch64' else '$rbp' pc_reg = '$pc' if arch == 'aarch64' else '$rip' fp = (argv[0] if len(argv) >= 1 else gdb.parse_and_eval(frame_reg)).cast(fp_type) rip = (argv[1] if len(argv) == 2 else gdb.parse_and_eval(pc_reg)).cast( T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print( frame.stringify( frame.create_php(idx=i, ar=fp.cast(ar_type), rip=rip))) else: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print( frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() fp = gdb.parse_and_eval('$rbp').cast(fp_type) rip = gdb.parse_and_eval('$rip').cast(T('uintptr_t')) if len(argv) >= 1: fp = argv[0].cast(fp_type) if len(argv) == 2: rip = argv[1].cast(T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print( frame.stringify( frame.create_php(idx=i, ar=fp.cast(ar_type), rip=rip))) else: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print( frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() try: arch = gdb.newest_frame().architecture().name() except: arch = 'i386:x86-64' frame_reg = '$x29' if arch == 'aarch64' else '$rbp' pc_reg = '$pc' if arch == 'aarch64' else '$rip' fp = (argv[0] if len(argv) >= 1 else gdb.parse_and_eval(frame_reg)).cast(fp_type) rip = (argv[1] if len(argv) == 2 else gdb.parse_and_eval(pc_reg)).cast(T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print(frame.stringify(frame.create_php( idx=i, ar=fp.cast(ar_type), rip=rip))) else: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print(frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkfp [fp] [rip]') return fp_type = T('uintptr_t').pointer() fp = gdb.parse_and_eval('$rbp').cast(fp_type) rip = gdb.parse_and_eval('$rip').cast(T('uintptr_t')) if len(argv) >= 1: fp = argv[0].cast(fp_type) if len(argv) == 2: rip = argv[1].cast(T('uintptr_t')) i = 0 fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) try: if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() print(frame.stringify(frame.create_php( idx=i, ar=fp.cast(ar_type), rip=rip))) else: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print(frame.stringify(frame.create_native(idx=i, fp=fp, rip=rip))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 1: print('Usage: walkstk [fp]') return # Set fp = $rbp. fp_type = T('uintptr_t').pointer() fp = gdb.parse_and_eval('$rbp').cast(fp_type) if len(argv) == 1: fp = argv[0].cast(fp_type)[0] # Set rip = $rip. rip_type = T('uintptr_t') rip = gdb.parse_and_eval('$rip').cast(rip_type) if len(argv) == 1: rip = argv[0].cast(fp_type)[1] # Find the starting native frame. native_frame = gdb.newest_frame() while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() if native_frame is None: if len(argv) == 0: print('walkstk: Unknown error: corrupt stack?') else: print('walkstk: Invalid frame pointer') return # Get the address and value of `mcg', the global MCGenerator pointer. # For some reason, gdb doesn't have debug info about the symbol, so we # can't use V(); probably this is because we declare it extern "C" (and # maybe also because we do so in a namespace). mcg_type = T('HPHP::jit::MCGenerator').pointer() mcg_addr = gdb.parse_and_eval('&::mcg').cast(mcg_type.pointer()) mcg = mcg_addr.dereference() # Set the bounds of the TC. try: tc_base = mcg['code']['m_base'] tc_end = tc_base + mcg['code']['m_codeSize'] except: # We can't access `mcg' for whatever reason. Assume that the TC is # above the data section, but restricted to low memory. tc_base = mcg_addr.cast(T('uintptr_t')) tc_end = 0x100000000 i = 0 # Make a fake frame for our `fp' and `rip'. This lets us pop a frame # at the top of the loop, which makes it easier to include the final # frame. fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) in_tc = rip >= tc_base and rip < tc_end # Try to get the PHP function name from the ActRec at %fp if we're # executing in the TC. if in_tc: ar_type = T('HPHP::ActRec').pointer() try: print( frame.stringify( frame.create_php(idx=i + 1, ar=fp.cast(ar_type), rip=rip))) except gdb.MemoryError: print( frame.stringify( frame.create_native(idx=i + 1, fp=fp, rip=rip, native_frame=native_frame))) # Pop native frames until we hit our caller's rip. else: frames = [] while (native_frame is not None and fp[1] != native_frame.pc()): frames.append( frame.create_native(idx=i, fp='{inline frame}', rip=rip, native_frame=native_frame)) i += 1 native_frame = native_frame.older() if frames: # Associate the frame pointer with the un-inlined frame. frames[-1]['fp'] = str(fp) for f in frames: print(frame.stringify(f))
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 1: print('Usage: walkstk [fp]') return # Bail early if the custom unwinder has not been set up. if not unwind.try_unwinder_init(): print('walkstk: Could not initialize the HHVM unwinder.') # Find the starting native frame. native_frame = gdb.newest_frame() if native_frame is None: print('walkstk: Cannot find any frames: corrupt stack?') return # Set fp = $rbp, rip = $rip. fp_type = T('uintptr_t').pointer() fp = native_frame.read_register('rbp').cast(fp_type) rip = native_frame.pc() if len(argv) == 1: # Start walking the stack from the user-provided `fp'. fp = argv[0].cast(fp_type)[0] rip = argv[0].cast(fp_type)[1] # Try to find a corresponding native frame. while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() i = 0 # Make a fake frame for our `fp' and `rip'. This lets us pop a frame # at the top of the loop, which makes it easier to include the final # frame. fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) # Try to get the PHP function name from the ActRec at `fp' if we're # executing in the TC. if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() try: print( frame.stringify( frame.create_php(idx=i, ar=fp.cast(ar_type), rip=rip))) except gdb.MemoryError: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, native_frame=native_frame))) if native_frame is not None: native_frame = native_frame.older() i += 1 else: if native_frame is None: # If we couldn't find a native frame, then `walkstk' was # invoked with a non-native `fp' argument. Now that we # don't seem to be in the TC, try to find the corresponding # native frame. native_frame = gdb.newest_frame() while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() if native_frame is not None: # Pop native frames until we hit our caller's rip. frames = [] while (native_frame is not None and (fp == 0x0 or fp[1] != native_frame.pc())): frames.append( frame.create_native(idx=i, fp='{inline frame}', rip=native_frame.pc(), native_frame=native_frame)) native_frame = native_frame.older() i += 1 if frames: # Associate the frame pointer with the un-inlined frame. frames[-1]['fp'] = str(fp) for f in frames: print(frame.stringify(f)) else: # We only hit this case if gdb undercounted the TC's # frames---which shouldn't happen unless the custom # unwinder (or gdb's unwinder API) is malfunctioning. # # Just guess that the name of the frame is the same as the # name of the block we're in. try: block = gdb.block_for_pc(int(rip)) name = block.function.name print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, name='? ' + name))) except: print( frame.stringify( frame.create_native(idx=i, fp=fp, rip=rip, native_frame=None))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkstk [sp] [rip]') return # Set sp = $rbp. sp_type = T('uintptr_t').pointer() sp = gdb.parse_and_eval('$rbp').cast(sp_type) if len(argv) >= 1: sp = argv[0].cast(sp_type) # Set rip = $rip. rip_type = T('uintptr_t') rip = gdb.parse_and_eval('$rip').cast(rip_type) if len(argv) == 2: rip = argv[1].cast(rip_type) try: mcg = V('HPHP::jit::mcg') tc_base = mcg['code']['m_base'] tc_end = tc_base + mcg['code']['m_codeSize'] except: mcg = None i = 0 native_frame = gdb.newest_frame() skip_tc = False # Only used when we can't find HPHP::mcg. # Munge `sp' so that it looks like the stack pointer that would point # to it if we had another frame---this lets us promote the "increment" # to the beginning of the loop, so that we don't miss the final frame. sp = (sp, rip) while sp: rip = sp[1] sp = sp[0].cast(sp_type) if mcg is not None: in_tc = rip >= tc_base and rip < tc_end elif not skip_tc: # TC frames look like unnamed normal native frames. try: next_frame = native_frame.older() in_tc = (next_frame is not None and next_frame.name() is None and next_frame.type() == gdb.NORMAL_FRAME) except AttributeError: # No older frame. in_tc = False else: in_tc = False skip_tc = False # Try to get the PHP function name from the ActRec at %sp if we're # executing in the TC. if in_tc: ar_type = T('HPHP::ActRec').pointer() try: print( frame.stringify( frame.create_php(idx=i + 1, ar=sp.cast(ar_type), rip=rip))) except gdb.MemoryError: if mcg is None: # We guessed wrong about whether we're in the TC. skip_tc = True print( frame.stringify( frame.create_native(idx=i + 1, sp=sp, rip=rip))) # Pop native frames until we find our current %sp. else: inlines = 0 while (native_frame is not None and rip != native_frame.pc()): if inlines > 0 and native_frame.name() is not None: print( frame.stringify( frame.create_native( idx=i, sp='{inline frame}', rip=rip, native_frame=native_frame))) i += 1 inlines += 1 native_frame = native_frame.older() print( frame.stringify( frame.create_native(idx=i, sp=sp, rip=rip, native_frame=native_frame)))
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 1: print('Usage: walkstk [fp]') return # Bail early if the custom unwinder has not been set up. if not unwind.try_unwinder_init(): print('walkstk: Could not initialize the HHVM unwinder.') # Find the starting native frame. native_frame = gdb.newest_frame() if native_frame is None: print('walkstk: Cannot find any frames: corrupt stack?') return # Set fp = $rbp, rip = $rip. fp_type = T('uintptr_t').pointer() fp = native_frame.read_register('rbp').cast(fp_type) rip = native_frame.pc() if len(argv) == 1: # Start walking the stack from the user-provided `fp'. fp = argv[0].cast(fp_type)[0] rip = argv[0].cast(fp_type)[1] # Try to find a corresponding native frame. while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() i = 0 # Make a fake frame for our `fp' and `rip'. This lets us pop a frame # at the top of the loop, which makes it easier to include the final # frame. fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) # Try to get the PHP function name from the ActRec at `fp' if we're # executing in the TC. if frame.is_jitted(fp, rip): ar_type = T('HPHP::ActRec').pointer() try: print(frame.stringify(frame.create_php( idx=i, ar=fp.cast(ar_type), rip=rip))) except gdb.MemoryError: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, native_frame=native_frame))) if native_frame is not None: native_frame = native_frame.older() i += 1 else: if native_frame is None: # If we couldn't find a native frame, then `walkstk' was # invoked with a non-native `fp' argument. Now that we # don't seem to be in the TC, try to find the corresponding # native frame. native_frame = gdb.newest_frame() while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() if native_frame is not None: # Pop native frames until we hit our caller's rip. frames = [] while (native_frame is not None and (fp == 0x0 or fp[1] != native_frame.pc())): frames.append(frame.create_native( idx=i, fp='{inline frame}', rip=native_frame.pc(), native_frame=native_frame)) native_frame = native_frame.older() i += 1 if frames: # Associate the frame pointer with the un-inlined frame. frames[-1]['fp'] = str(fp) for f in frames: print(frame.stringify(f)) else: # We only hit this case if gdb undercounted the TC's # frames---which shouldn't happen unless the custom # unwinder (or gdb's unwinder API) is malfunctioning. # # Just guess that the name of the frame is the same as the # name of the block we're in. try: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, name=_function_for(rip)))) except: print(frame.stringify(frame.create_native( idx=i, fp=fp, rip=rip, native_frame=None))) i += 1
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 2: print('Usage: walkstk [sp] [rip]') # Set sp = $rbp. sp_type = T('uintptr_t').pointer() sp = gdb.parse_and_eval('$rbp').cast(sp_type) if len(argv) >= 1: sp = argv[0].cast(sp_type) # Set rip = $rip. rip_type = T('uintptr_t') rip = gdb.parse_and_eval('$rip').cast(rip_type) if len(argv) == 2: rip = argv[1].cast(rip_type) try: mcg = V('HPHP::jit::mcg') tc_base = mcg['code']['m_base'] tc_end = tc_base + mcg['code']['m_codeSize'] except: mcg = None i = 0 native_frame = gdb.newest_frame() skip_tc = False # Only used when we can't find HPHP::mcg. # Munge `sp' so that it looks like the stack pointer that would point # to it if we had another frame---this lets us promote the "increment" # to the beginning of the loop, so that we don't miss the final frame. sp = (sp, rip) while sp: rip = sp[1] sp = sp[0].cast(sp_type) if mcg is not None: in_tc = rip >= tc_base and rip < tc_end elif not skip_tc: # TC frames look like unnamed normal native frames. try: next_frame = native_frame.older() in_tc = (next_frame is not None and next_frame.name() is None and next_frame.type() == gdb.NORMAL_FRAME) except AttributeError: # No older frame. in_tc = False else: in_tc = False skip_tc = False # Try to get the PHP function name from the ActRec at %sp if we're # executing in the TC. if in_tc: ar_type = T('HPHP::ActRec').pointer() try: print(frame.stringify(frame.create_php( idx=i + 1, ar=sp.cast(ar_type), rip=rip))) except gdb.MemoryError: if mcg is None: # We guessed wrong about whether we're in the TC. skip_tc = True print(frame.stringify(frame.create_native( idx=i + 1, sp=sp, rip=rip))) # Pop native frames until we find our current %sp. else: inlines = 0 while (native_frame is not None and rip != native_frame.pc()): if inlines > 0 and native_frame.name() is not None: print(frame.stringify(frame.create_native( idx=i, sp='{inline frame}', rip=rip, native_frame=native_frame))) i += 1 inlines += 1 native_frame = native_frame.older() print(frame.stringify(frame.create_native( idx=i, sp=sp, rip=rip, native_frame=native_frame)))
def invoke(self, args, from_tty): argv = parse_argv(args) if len(argv) > 1: print('Usage: walkstk [fp]') return # Set fp = $rbp. fp_type = T('uintptr_t').pointer() fp = gdb.parse_and_eval('$rbp').cast(fp_type) if len(argv) == 1: fp = argv[0].cast(fp_type)[0] # Set rip = $rip. rip_type = T('uintptr_t') rip = gdb.parse_and_eval('$rip').cast(rip_type) if len(argv) == 1: rip = argv[0].cast(fp_type)[1] # Find the starting native frame. native_frame = gdb.newest_frame() while (native_frame is not None and rip != native_frame.pc()): native_frame = native_frame.older() if native_frame is None: if len(argv) == 0: print('walkstk: Unknown error: corrupt stack?') else: print('walkstk: Invalid frame pointer') return # Get the address and value of `mcg', the global MCGenerator pointer. # For some reason, gdb doesn't have debug info about the symbol, so we # can't use V(); probably this is because we declare it extern "C" (and # maybe also because we do so in a namespace). mcg_type = T('HPHP::jit::MCGenerator').pointer() mcg_addr = gdb.parse_and_eval('&::mcg').cast(mcg_type.pointer()) mcg = mcg_addr.dereference() # Set the bounds of the TC. try: tc_base = mcg['code']['m_base'] tc_end = tc_base + mcg['code']['m_codeSize'] except: # We can't access `mcg' for whatever reason. Assume that the TC is # above the data section, but restricted to low memory. tc_base = mcg_addr.cast(T('uintptr_t')) tc_end = 0x100000000 i = 0 # Make a fake frame for our `fp' and `rip'. This lets us pop a frame # at the top of the loop, which makes it easier to include the final # frame. fp = (fp, rip) while fp: rip = fp[1] fp = fp[0].cast(fp_type) in_tc = rip >= tc_base and rip < tc_end # Try to get the PHP function name from the ActRec at %fp if we're # executing in the TC. if in_tc: ar_type = T('HPHP::ActRec').pointer() try: print(frame.stringify(frame.create_php( idx=i + 1, ar=fp.cast(ar_type), rip=rip))) except gdb.MemoryError: print(frame.stringify(frame.create_native( idx=i + 1, fp=fp, rip=rip, native_frame=native_frame))) # Pop native frames until we hit our caller's rip. else: frames = [] while (native_frame is not None and fp[1] != native_frame.pc()): frames.append(frame.create_native( idx=i, fp='{inline frame}', rip=rip, native_frame=native_frame)) i += 1 native_frame = native_frame.older() if frames: # Associate the frame pointer with the un-inlined frame. frames[-1]['fp'] = str(fp) for f in frames: print(frame.stringify(f))