tools/gdb-helpers.py (182 lines of code) (raw):

# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at ## # http://www.apache.org/licenses/LICENSE-2.0 ## # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script will add a command to help print out ATS data structures in gdb. # # In a .gdbinit file or from the gdb command prompt: # source <path to this file>.py # # Then you can type: `atspr sm this` Where this is a HttpSM* and it will print some information about it. # # For the expression you should be able to put anything that can be cast to the appropriate type. # # Type `atspr` by itself for a list of printables. # import gdb import socket import struct from curses.ascii import isgraph # return memory for an ats string address and length def ats_str(addr, addr_len): if addr_len == 0: return '' #print("addr {} len {}".format(addr, addr_len)) inferior = gdb.selected_inferior() try: buff = inferior.read_memory(addr, addr_len) if buff[0] == '\x00': return 'null' else: return buff except BaseException: return 'unreadable({:x})'.format(int(addr)) def hdrtoken(idx): hdrtokens = gdb.parse_and_eval('hdrtoken_strs') return hdrtokens[idx].string() def wks_or_str(idx, addr, addr_len): if idx >= 0: return hdrtoken(idx) else: return ats_str(addr, addr_len) class URL: def __init__(self, val): self.impl = val['m_url_impl'].dereference() def __str__(self): return "{}://{}/{}".format(self.scheme(), self.host(), self.path()) def scheme(self): return ats_str(self.impl['m_ptr_scheme'], self.impl['m_len_scheme']) def host(self): return ats_str(self.impl['m_ptr_host'], self.impl['m_len_host']) def path(self): return ats_str(self.impl['m_ptr_path'], self.impl['m_len_path']) class HTTPHdr: def __init__(self, val): self.impl = val['m_http'] self.val = val def url(self): return URL(self.val['m_url_cached']) def is_request(self): pol = self.impl['m_polarity'] return pol == 1 def is_response(self): pol = self.impl['m_polarity'] return pol == 2 def is_valid(self): if self.impl == 0: return None pol = self.impl['m_polarity'] return pol > 0 def method(self): idx = self.impl['u']['req']['m_method_wks_idx'] if idx >= 0: return hdrtoken(idx) else: return ats_str(self.impl['u']['req']['m_ptr_method'], self.impl['u']['req']['m_len_method']) def status(self): return self.impl['u']['resp']['m_status'] def headers(self): mime = self.val['m_mime'].dereference() fblock_ptr = mime['m_first_fblock'].address while fblock_ptr != 0: fblock = fblock_ptr.dereference() slots = fblock['m_field_slots'] #print("slots type {} address {} size {}".format(slots.type, slots.address, slots.type.sizeof)) #print("next idx {} len {} next {}".format(fblock['m_freetop'], fblock['m_length'], fblock['m_next'])) for slot_idx in range(fblock['m_freetop']): fld = slots[slot_idx] wks = fld['m_wks_idx'] name = wks_or_str(wks, fld['m_ptr_name'], fld['m_len_name']) yield (name, ats_str(fld['m_ptr_value'], fld['m_len_value'])) fblock_ptr = fblock['m_next'] def pr(self): if self.is_valid(): if self.is_request(): print("{} {}".format(self.method(), self.url())) if self.is_response(): print("status: {}".format(self.status())) for key, val in self.headers(): print("{}: {}".format(key, val)) else: print("invalid") class ConnectionAttributes: def __init__(self, val): self.val = val def http_version(self): v = self.val['http_version']['m_version'] return (v >> 16, v & 0xffff) def src_addr(self): ip = self.val['src_addr'] s = struct.pack('!I', socket.ntohl(int(ip['sin']['sin_addr']['s_addr']))) return socket.inet_ntoa(s) def dst_addr(self): ip = self.val['dst_addr'] s = struct.pack('!I', socket.ntohl(int(ip['sin']['sin_addr']['s_addr']))) return socket.inet_ntoa(s) class HttpSM: def __init__(self, val): ptr_type = gdb.lookup_type('struct HttpSM').pointer() self.val = None if val.type != ptr_type: self.val = val.cast(ptr_type) else: self.val = val def id(self): return self.val['sm_id'] def client_request(self): return HTTPHdr(self.val['t_state']['hdr_info']['client_request']) def client_response(self): return HTTPHdr(self.val['t_state']['hdr_info']['client_response']) def server_request(self): return HTTPHdr(self.val['t_state']['hdr_info']['server_request']) def server_response(self): return HTTPHdr(self.val['t_state']['hdr_info']['server_response']) def transform_response(self): return HTTPHdr(self.val['t_state']['hdr_info']['transform_response']) def cache_response(self): return HTTPHdr(self.val['t_state']['hdr_info']['cache_response']) def client_info(self): return ConnectionAttributes(self.val['t_state']['client_info']) def parent_info(self): return ConnectionAttributes(self.val['t_state']['client_info']) def server_info(self): return ConnectionAttributes(self.val['t_state']['client_info']) def sm_command(val): sm = HttpSM(val) print('smid = %s' % sm.id()) print('---- request ----') sm.client_request().pr() print('---- response ----') sm.client_response().pr() print('---- server_request ----') sm.server_request().pr() print('---- server_response ----') sm.server_response().pr() print('---- transform_response ----') sm.transform_response().pr() print('---- cache_response ----') sm.cache_response().pr() print('---- client_info ----') info = sm.client_info() major, minor = info.http_version() print("HTTP {}.{} ({} -> {})".format(major, minor, info.src_addr(), info.dst_addr())) print('---- server_info ----') info = sm.server_info() major, minor = info.http_version() print("HTTP {}.{} ({} -> {})".format(major, minor, info.src_addr(), info.dst_addr())) print('---- parent_info ----') info = sm.parent_info() major, minor = info.http_version() print("HTTP {}.{} ({} -> {})".format(major, minor, info.src_addr(), info.dst_addr())) def hdrs_command(val): HTTPHdr(val).pr() def url_command(val): print(URL(val)) commands = [ ("sm", sm_command, "Print HttpSM details (type HttpSM*)"), ("hdrs", hdrs_command, "Print HttpHdr details (type HTTPHdr)"), ("url", url_command, "Print URL"), ] def usage(): print("Usage: atspr <command> <expr>") print("commands:") for cmd, f, desc in commands: print(" {}: {}".format(cmd, desc)) class ATSPrintCommand(gdb.Command): def __init__(self): super(ATSPrintCommand, self).__init__('atspr', gdb.COMMAND_DATA) def invoke(self, arg, from_tty): argv = gdb.string_to_argv(arg) if len(argv) < 2: usage() return what = argv[0] expr = argv[1] val = gdb.parse_and_eval(expr) for cmd, f, _ in commands: if cmd == what: f(val) return usage() ATSPrintCommand() # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4