1############################################################################ 2# 3# Copyright (C) 2021 The Qt Company Ltd. 4# Contact: https://www.qt.io/licensing/ 5# 6# This file is part of Qt Creator. 7# 8# Commercial License Usage 9# Licensees holding valid commercial Qt licenses may use this file in 10# accordance with the commercial license agreement provided with the 11# Software or, alternatively, in accordance with the terms contained in 12# a written agreement between you and The Qt Company. For licensing terms 13# and conditions see https://www.qt.io/terms-conditions. For further 14# information use the contact form at https://www.qt.io/contact-us. 15# 16# GNU General Public License Usage 17# Alternatively, this file may be used under the terms of the GNU 18# General Public License version 3 as published by the Free Software 19# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20# included in the packaging of this file. Please review the following 21# information to ensure the GNU General Public License requirements will 22# be met: https://www.gnu.org/licenses/gpl-3.0.html. 23# 24############################################################################ 25 26import gdb 27import sys 28import time 29 30# for ProcessName capture 31try: 32 import psutil 33except: 34 psutil = None 35 36# Caps types 37Address, \ 38Caller, \ 39Callstack, \ 40FilePos, \ 41Function, \ 42Pid, \ 43ProcessName, \ 44Tick, \ 45Tid, \ 46ThreadName, \ 47Expression, \ 48 = range(0, 11) 49 50class GDBTracepoint(gdb.Breakpoint): 51 """ 52 Python Breakpoint extension for "tracepoints", breakpoints that do not stop the inferior 53 """ 54 55 @staticmethod 56 def create(args, onModified, onHit, onExpression): 57 """ 58 Static creator function 59 """ 60 tp_kwargs = {} 61 if 'temporary' in args.keys(): 62 tp_kwargs['temporary'] = args['temporary'] 63 spec = args['spec'] 64 tp = GDBTracepoint(spec, **tp_kwargs) 65 tp.onModified = onModified 66 tp.onHit = onHit 67 tp.onExpression = onExpression 68 if 'ignore_count' in args.keys(): 69 tp.ignore_count = args['ignore_count'] 70 if 'enabled' in args.keys(): 71 tp.enabled = args['enabled'] 72 if 'thread' in args.keys(): 73 tp.thread = args['thread'] 74 if 'condition' in args.keys(): 75 tp.condition = args['condition'] 76 if 'caps' in args.keys(): 77 for ce in args['caps']: 78 tp.addCaps(ce[0], ce[1]) 79 return tp 80 81 def __init__(self, spec, **kwargs): 82 """ 83 Constructor 84 """ 85 kwargs['internal'] = True 86 super(GDBTracepoint, self).__init__(spec, **kwargs) 87 self.caps = [] 88 89 _hexSize = 8 if sys.maxsize > 2**32 else 4 90 _hasMonotonicTime = False if sys.version_info[0] <= 2 or (sys.version_info[0] == 3 and sys.version_info[1] < 3) else True 91 92 def dicts(self): 93 """ 94 Returns dictionareis for mi representation 95 """ 96 results = [] 97 result = {} 98 result['number'] = str(self.number) 99 result['enabled'] = 'y' if self.enabled else 'n' 100 result['type'] = 'pseudo_tracepoint' 101 result['disp'] = 'del' if self.temporary else 'keep' 102 result['times'] = str(self.hit_count) 103 result['original-location'] = self.location 104 try: 105 d = gdb.decode_line(self.location) 106 if d[1] is None: 107 result['addr'] = '<PENDING>' 108 result['pending'] = self.location 109 results.append(result) 110 else: 111 if len(d[1]) > 1: 112 result['addr'] = '<MULTIPLE>' 113 results.append(result) 114 for i, sl in enumerate(d[1]): 115 result_ = {} 116 result_['number'] = result['number'] + "." + str(i + 1) 117 result_['enabled'] = 'y' if self.enabled else 'n' 118 if sl.pc is None: 119 result_['addr'] = '<no address>' 120 else: 121 result_['addr'] = '{0:#0{1}x}'.format(sl.pc, self._hexSize + 2) 122 if sl.symtab and sl.symtab.is_valid(): 123 func = self._getFunctionFromAddr(sl.pc) 124 if func: 125 result_['func'] = func.print_name 126 result_['file'] = sl.symtab.filename 127 result_['fullname'] = sl.symtab.fullname() 128 result_['line'] = sl.line 129 results.append(result_) 130 else: 131 sl = d[1][0] 132 if sl.pc is None: 133 result['addr'] = '<no address>' 134 else: 135 result['addr'] = '{0:#0{1}x}'.format(sl.pc, self._hexSize + 2) 136 if sl.symtab and sl.symtab.is_valid(): 137 func = self._getFunctionFromAddr(sl.pc) 138 if func: 139 result['func'] = func.print_name 140 result['file'] = sl.symtab.filename 141 result['fullname'] = sl.symtab.fullname() 142 result['line'] = sl.line 143 results.append(result) 144 except Exception as e: 145 import traceback 146 traceback.print_exc() 147 result['addr'] = '<PENDING>' 148 result['pending'] = self.location 149 results.append(result) 150 return results 151 152 def addCaps(self, capsType, expression=None): 153 """ 154 Adds capture expressions for a tracepoint 155 156 :param caps_type: Type of capture 157 :param expression: Expression for Expression caps type 158 """ 159 if capsType != Expression: 160 expression = None 161 else: 162 if expression is None: 163 expression = '' 164 self.caps.append((self.capsMap[capsType], expression)) 165 166 def stop(self): 167 """ 168 Overridden stop function, this evaluates conditions and captures data from the inferior 169 170 :return: Always False 171 """ 172 try: 173 self.onModified(self) 174 result = {} 175 result['number'] = self.number 176 try: 177 if self.condition: 178 try: 179 result = gdb.parse_and_eval(self.condition) 180 if result.type.code == gdb.TYPE_CODE_BOOL and str(result) == 'false': 181 return False 182 except: 183 pass 184 if self.ignore_count > 0: 185 return False 186 if self.thread and gdb.selected_thread().global_num != self.thread: 187 return False 188 except Exception as e: 189 result['warning'] = str(e) 190 self.onHit(self, result) 191 return False 192 if len(self.caps) > 0: 193 caps = [] 194 try: 195 for func, expr in self.caps: 196 if expr is None: 197 caps.append(func(self)) 198 else: 199 caps.append(func(self, expr)) 200 except Exception as e: 201 result['warning'] = str(e) 202 self.onHit(self, result) 203 return False 204 result['caps'] = caps 205 self.onHit(self, result) 206 return False 207 except: 208 # Always return false, regardless... 209 return False 210 211 def _getFunctionFromAddr(self, addr): 212 try: 213 block = gdb.block_for_pc(addr) 214 while block and not block.function: 215 block = block.superblock 216 if block is None: 217 return None 218 return block.function 219 except: 220 return None 221 222 def _getAddress(self): 223 """ 224 Capture function for Address 225 """ 226 try: 227 frame = gdb.selected_frame() 228 if not (frame is None) and (frame.is_valid()): 229 return '{0:#0{1}x}'.format(frame.pc(), self._hexSize + 2) 230 except Exception as e: 231 return str(e) 232 return '<null address>' 233 234 def _getCaller(self): 235 """ 236 Capture function for Caller 237 """ 238 try: 239 frame = gdb.selected_frame() 240 if not (frame is None) and (frame.is_valid()): 241 frame = frame.older() 242 if not (frame is None) and (frame.is_valid()): 243 name = frame.name() 244 if name is None: 245 return '<unknown caller>' 246 return name 247 except Exception as e: 248 return str(e) 249 return '<unknown caller>' 250 251 def _getCallstack(self): 252 """ 253 Capture function for Callstack 254 """ 255 try: 256 frames = [] 257 frame = gdb.selected_frame() 258 if (frame is None) or (not frame.is_valid()): 259 frames.append('<unknown frame>') 260 return str(frames) 261 while not (frame is None): 262 func = frame.function() 263 if func is None: 264 frames.append('{0:#0{1}x}'.format(frame.pc(), self._hexSize + 2)) 265 else: 266 sl = frame.find_sal() 267 if sl is None: 268 frames.append(func.symtab.filename) 269 else: 270 frames.append(func.symtab.filename + ':' + str(sl.line)) 271 frame = frame.older() 272 return frames 273 except Exception as e: 274 return str(e) 275 276 def _getFilePos(self): 277 """ 278 Capture function for FilePos 279 """ 280 try: 281 frame = gdb.selected_frame() 282 if (frame is None) or (not frame.is_valid()): 283 return '<unknown file pos>' 284 sl = frame.find_sal() 285 if sl is None: 286 return '<unknown file pos>' 287 return sl.symtab.filename + ':' + str(sl.line) 288 except Exception as e: 289 return str(e) 290 291 def _getFunction(self): 292 """ 293 Capture function for Function 294 """ 295 try: 296 frame = gdb.selected_frame() 297 if not (frame is None): 298 return str(frame.name()) 299 except Exception as e: 300 return str(e) 301 return '<unknown function>' 302 303 def _getPid(self): 304 """ 305 Capture function for Pid 306 """ 307 try: 308 thread = gdb.selected_thread() 309 if not (thread is None): 310 (pid, lwpid, tid) = thread.ptid 311 return str(pid) 312 except Exception as e: 313 return str(e) 314 return '<unknown pid>' 315 316 def _getProcessName(slef): 317 """ 318 Capture for ProcessName 319 """ 320 # gdb does not expose process name, neither does (standard) python 321 # You can use for example psutil, but it might not be present. 322 # Default to name of thread with ID 1 323 inf = gdb.selected_inferior() 324 if psutil is None: 325 try: 326 if inf is None: 327 return '<unknown process name>' 328 threads = filter(lambda t: t.num == 1, list(inf.threads())) 329 if len(threads) < 1: 330 return '<unknown process name>' 331 thread = threads[0] 332 # use thread name 333 return thread.name 334 except Exception as e: 335 return str(e) 336 else: 337 return psutil.Process(inf.pid).name() 338 339 def _getTick(self): 340 """ 341 Capture function for Tick 342 """ 343 if self._hasMonotonicTime: 344 return str(int(time.monotonic() * 1000)) 345 else: 346 return '<monotonic time not available>' 347 348 def _getTid(self): 349 """ 350 Capture function for Tid 351 """ 352 try: 353 thread = gdb.selected_thread() 354 if not (thread is None): 355 (pid, lwpid, tid) = thread.ptid 356 if tid == 0: 357 return str(lwpid) 358 else: 359 return str(tid) 360 except Exception as e: 361 return str(e) 362 return '<unknown tid>' 363 364 def _getThreadName(self): 365 """ 366 Capture function for ThreadName 367 """ 368 try: 369 thread = gdb.selected_thread() 370 if not (thread is None): 371 return str(thread.name) 372 except Exception as e: 373 return str(e) 374 return '<unknown thread name>' 375 376 def _getExpression(self, expression): 377 """ 378 Capture function for Expression 379 380 :param expr: The expression to evaluate 381 """ 382 try: 383 value = gdb.parse_and_eval(expression) 384 if value: 385 return self.onExpression(self, expression, value) 386 except Exception as e: 387 return self.onExpression(self, expression, e) 388 389 capsMap = {Address: _getAddress, 390 Caller: _getCaller, 391 Callstack: _getCallstack, 392 FilePos: _getFilePos, 393 Function: _getFunction, 394 Pid: _getPid, 395 ProcessName: _getProcessName, 396 Tid: _getTid, 397 Tick: _getTick, 398 ThreadName: _getThreadName, 399 Expression: _getExpression} 400