source: doc/theses/lynn_tran_SE499/SE499-master/utils-gdb.py@ 1b34b87

ADT aaron-thesis arm-eh ast-experimental cleanup-dtors deferred_resn enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr no_list persistent-indexer pthread-emulation qualifiedEnum
Last change on this file since 1b34b87 was 1b34b87, checked in by Peter A. Buhr <pabuhr@…>, 7 years ago

Lynn's GDB essay

  • Property mode set to 100644
File size: 21.9 KB
Line 
1"""
2To run this extension, the python name has to be as same as one of the loaded library
3Additionally, the file must exist in a folder which is in gdb's safe path
4"""
5import collections
6import gdb
7
8# set these signal handlers with some settings (nostop, noprint, pass)
9gdb.execute('handle SIGALRM nostop noprint pass')
10gdb.execute('handle SIGUSR1 nostop noprint pass')
11
12# GDB types for various structures/types in uC++
13uCluster_ptr_type = gdb.lookup_type('uCluster').pointer()
14uClusterDL_ptr_type = gdb.lookup_type('uClusterDL').pointer()
15uBaseTask_ptr_type = gdb.lookup_type('uBaseTask').pointer()
16uBaseTaskDL_ptr_type = gdb.lookup_type('uBaseTaskDL').pointer()
17int_ptr_type = gdb.lookup_type('int').pointer()
18
19# A named tuple representing information about a stack
20StackInfo = collections.namedtuple('StackInfo', 'sp fp pc')
21
22# A global variable to keep track of stack information as one context switches
23# from one task to another task
24STACK = []
25
26# A global variable to keep all system task name
27SysTask_Name = ["uLocalDebuggerReader", "uLocalDebugger", "uProcessorTask", "uBootTask", "uSystemTask",
28"uProcessorTask", "uPthread", "uProfiler"]
29
30def get_addr(addr):
31 """
32 NOTE: sketchy solution to retrieve address. There is a better solution...
33 @addr: str of an address that can be in a format 0xfffff <type of the object
34 at this address>
35 Return: str of just the address
36 """
37 str_addr = str(addr)
38 ending_addr_index = str_addr.find('<')
39 if ending_addr_index == -1:
40 return str(addr)
41 return str_addr[:ending_addr_index].strip()
42
43def print_usage(msg):
44 """
45 Print out usage message
46 @msg: str
47 """
48 print('Usage: ' + msg)
49
50def get_argv_list(args):
51 """
52 Split the argument list in string format, where each argument is separated
53 by whitespace delimiter, to a list of arguments like argv
54 @args: str of arguments
55 Return:
56 [] if args is an empty string
57 list if args is not empty
58 """
59 # parse the string format of arguments and return a list of arguments
60 argv = args.split(' ')
61 if len(argv) == 1 and argv[0] == '':
62 return []
63 return argv
64
65def get_cluster_root():
66 """
67 Return: gdb.Value of globalClusters.root (is an address)
68 """
69 cluster_root = gdb.parse_and_eval('uKernelModule::globalClusters.root')
70 if cluster_root.address == 0x0:
71 print('No clusters, program terminated')
72 return cluster_root
73
74def lookup_cluster_by_name(cluster_name):
75 """
76 Look up a cluster given its ID
77 @cluster_name: str
78 Return: gdb.Value
79 """
80 cluster_root = get_cluster_root()
81 if cluster_root.address == 0x0:
82 return cluster_root.address
83
84 # lookup for the task associated with the id
85 cluster = 0x0
86 curr = cluster_root
87 while True:
88 if curr['cluster_']['name'].string() == cluster_name:
89 cluster = curr['cluster_'].address
90 break
91 curr = curr['next'].cast(uClusterDL_ptr_type)
92 if curr == cluster_root:
93 break
94
95 if cluster == 0x0:
96 print("Cannot find a cluster with the name: {}.".format(cluster_name))
97 return cluster
98
99############################ COMMAND IMPLEMENTATION #########################
100
101class Clusters(gdb.Command):
102 """Print out the list of available clusters"""
103 usage_msg = 'cluster'
104 def __init__(self):
105 super(Clusters, self).__init__('clusters', gdb.COMMAND_USER)
106
107 def invoke(self, arg, from_tty):
108 """
109 Iterate through a circular linked list of clusters and print out its
110 name along with address associated to each cluster
111 @arg: str
112 @from_tty: bool
113 """
114 argv = get_argv_list(arg)
115 if len(argv) != 0:
116 print_usage(self.usage_msg)
117 return
118
119 cluster_root = get_cluster_root()
120 if cluster_root.address == 0x0:
121 return
122 curr = cluster_root
123 print('{:>20}{:>18}'.format('Name', 'Address'))
124
125 while True:
126 print('{:>20}{:>18}'.format(curr['cluster_']['name'].string(),
127 str(curr['cluster_'].reference_value())[1:]))
128 curr = curr['next'].cast(uClusterDL_ptr_type)
129 if curr == cluster_root:
130 break
131
132class ClusterProcessors(gdb.Command):
133 """Display a list of all info about all available processors on a particular cluster"""
134 usage_msg = 'processors <cluster_name>'
135 def __init__(self):
136 super(ClusterProcessors, self).__init__('processors', gdb.COMMAND_USER)
137
138 def invoke(self, arg, from_tty):
139 """
140 Iterate through a circular linked list of tasks and print out all
141 info about each processor in that cluster
142 @arg: str
143 @from_tty: bool
144 """
145 argv = get_argv_list(arg)
146 if len(argv) > 1:
147 print_usage(self.usage_msg)
148 return
149
150 if len(argv) == 0:
151 cluster_address = lookup_cluster_by_name("userCluster")
152 else:
153 cluster_address = lookup_cluster_by_name(argv[0])
154
155 if cluster_address == 0x0:
156 return
157
158 processor_root = cluster_address.cast(uCluster_ptr_type)['processorsOnCluster']['root']
159 if processor_root.address == 0x0:
160 print('There are no processors for cluster at address: {}'.format(cluster_address))
161 return
162
163 uProcessorDL_ptr_type = gdb.lookup_type('uProcessorDL').pointer()
164 print('{:>18}{:>20}{:>20}{:>20}'.format('Address', 'PID', 'Preemption', 'Spin'))
165 curr = processor_root
166
167 while True:
168 processor = curr['processor_']
169 print('{:>18}{:>20}{:>20}{:>20}'.format(get_addr(processor.address),
170 str(processor['pid']), str(processor['preemption']),
171 str(processor['spin'])))
172
173 curr = curr['next'].cast(uProcessorDL_ptr_type)
174 if curr == processor_root:
175 break
176
177class Task(gdb.Command):
178 usage_msg = """
179 task : print userCluster tasks, application tasks only
180 task <clusterName> : print cluster tasks, application tasks only
181 task all : print all clusters, all tasks
182 task <id> : context switch to task id on userCluster
183 task 0x<address> : context switch to task on any cluster
184 task <id> <clusterName> : context switch to task on specified cluster
185 """
186 def __init__(self):
187 # The first parameter of the line below is the name of the command. You
188 # can call it 'uc++ task'
189 super(Task, self).__init__('task', gdb.COMMAND_USER)
190
191 ############################ AUXILIARY FUNCTIONS #########################
192
193 def print_tasks_by_cluster_all(self, cluster_address):
194 """
195 Display a list of all info about all available tasks on a particular cluster
196 @cluster_address: gdb.Value
197 """
198 cluster_address = cluster_address.cast(uCluster_ptr_type)
199 task_root = cluster_address['tasksOnCluster']['root']
200
201 if task_root.address == 0x0:
202 print('There are no tasks for cluster at address: {}'.format(cluster_address))
203 return
204
205 print('{:>4}{:>20}{:>18}{:>25}'.format('ID', 'Task Name', 'Address', 'State'))
206 curr = task_root
207 task_id = 0
208 systask_id = -1
209
210 btstr = gdb.execute('bt', to_string = True)
211 break_addr = btstr.splitlines()[0].split('this=',1)[1].split(',')[0].split(')')[0]
212
213 while True:
214 global SysTask_Name
215 if (curr['task_']['name'].string() in SysTask_Name):
216 if str(curr['task_'].reference_value())[1:] == break_addr:
217 print(
218 ('{:>4}{:>20}{:>18}{:>25}'.format('* '+str(systask_id), curr['task_']['name'].string(),
219 str(curr['task_'].reference_value())[1:],
220 str(curr['task_']['state']))
221 )
222 )
223 else:
224 print(
225 ('{:>4}{:>20}{:>18}{:>25}'.format(systask_id, curr['task_']['name'].string(),
226 str(curr['task_'].reference_value())[1:],
227 str(curr['task_']['state']))
228 )
229 )
230 systask_id -= 1
231 else:
232 if str(curr['task_'].reference_value())[1:] == break_addr:
233 print('{:>4}{:>20}{:>18}{:>25}'.format('* '+str(task_id), curr['task_']['name'].string(),
234 str(curr['task_'].reference_value())[1:],
235 str(curr['task_']['state']))
236 )
237 else:
238 print('{:>4}{:>20}{:>18}{:>25}'.format(task_id, curr['task_']['name'].string(),
239 str(curr['task_'].reference_value())[1:],
240 str(curr['task_']['state']))
241 )
242 task_id += 1
243
244 curr = curr['next'].cast(uBaseTaskDL_ptr_type)
245 if curr == task_root:
246 break
247
248 def print_tasks_by_cluster_address_all(self, cluster_address):
249 """
250 Display a list of all info about all available tasks on a particular cluster
251 @cluster_address: str
252 """
253 # Iterate through a circular linked list of tasks and print out its
254 # name along with address associated to each cluster
255
256 # convert hex string to hex number
257 try:
258 hex_addr = int(cluster_address, 16)
259 except:
260 print_usage(self.usage_msg)
261 return
262
263 cluster_address = gdb.Value(hex_addr)
264 self.print_tasks_by_cluster_all(cluster_address)
265
266 def print_tasks_by_cluster_address(self, cluster_address):
267 """
268 Display a list of limited info about all available tasks on a particular cluster
269 @cluster_address: str
270 """
271 # Iterate through a circular linked list of tasks and print out its
272 # name along with address associated to each cluster
273
274 # convert hex string to hex number
275 try:
276 hex_addr = int(cluster_address, 16)
277 except:
278 print_usage(self.usage_msg)
279 return
280
281 cluster_address = gdb.Value(hex_addr).cast(uCluster_ptr_type)
282 task_root = cluster_address['tasksOnCluster']['root']
283
284 if task_root.address == 0x0:
285 print('There are no tasks for cluster at address: {}'.format(cluster_address))
286 return
287
288 print('{:>4}{:>20}{:>18}{:>25}'.format('ID', 'Task Name', 'Address', 'State'))
289 curr = task_root
290 task_id = 0
291 btstr = gdb.execute('bt', to_string = True)
292 break_addr = btstr.splitlines()[0].split('this=',1)[1].split(',')[0].split(')')[0]
293
294 while True:
295 global SysTask_Name
296 if (curr['task_']['name'].string() not in SysTask_Name):
297 if str(curr['task_'].reference_value())[1:] == break_addr:
298 print(
299 ('{:>4}{:>20}{:>18}{:>25}'.format('* '+str(task_id), curr['task_']['name'].string(),
300 str(curr['task_'].reference_value())[1:],
301 str(curr['task_']['state']))
302 )
303 )
304 else:
305 print('{:>4}{:>20}{:>18}{:>25}'.format(task_id, curr['task_']['name'].string(),
306 str(curr['task_'].reference_value())[1:],
307 str(curr['task_']['state']))
308 )
309
310 curr = curr['next'].cast(uBaseTaskDL_ptr_type)
311 task_id += 1
312 if curr == task_root:
313 break
314 else:
315 curr = curr['next'].cast(uBaseTaskDL_ptr_type)
316 if curr == task_root:
317 break
318
319 ############################ COMMAND FUNCTIONS #########################
320
321 def print_user_tasks(self):
322 """Iterate only userCluster, print only tasks and main"""
323
324 cluster_address = lookup_cluster_by_name("userCluster")
325 if cluster_address == 0x0:
326 return
327
328 self.print_tasks_by_cluster_address(str(cluster_address))
329
330
331 def print_all_tasks(self):
332 """Iterate through each cluster, iterate through all tasks and print out info about all the tasks
333 in those clusters"""
334 cluster_root = get_cluster_root()
335 if cluster_root.address == 0x0:
336 return
337
338 curr = cluster_root
339 print('{:>20}{:>18}'.format('Cluster Name', 'Address'))
340
341 while True:
342 addr = str(curr['cluster_'].reference_value())[1:]
343 print('{:>20}{:>18}'.format(curr['cluster_']['name'].string(), addr))
344
345 self.print_tasks_by_cluster_address_all(addr)
346 curr = curr['next'].cast(uClusterDL_ptr_type)
347 if curr == cluster_root:
348 break
349
350 def pushtask_by_address(self, task_address):
351 """Change to a new task by switching to a different stack and manually
352 adjusting sp, fp and pc
353 @task_address: str
354 2 supported format:
355 in hex format
356 <hex_address>: literal hexadecimal address
357 Ex: 0xffffff
358 in name of the pointer to the task
359 "task_name": pointer of the variable name of the cluster
360 Ex: T* s -> task_name = s
361 Return: gdb.value of the cluster's address
362 """
363 # Task address has a format "task_address", which implies that it is the
364 # name of the variable, and it needs to be evaluated
365 if task_address.startswith('"') and task_address.endswith('"'):
366 task = gdb.parse_and_eval(task_address.replace('"', ''))
367 else:
368 # Task address format does not include the quotation marks, which implies
369 # that it is a hex address
370 # convert hex string to hex number
371 try:
372 hex_addr = int(task_address, 16)
373 except:
374 print_usage(self.usage_msg)
375 return
376 task_address = gdb.Value(hex_addr)
377 task = task_address.cast(uBaseTask_ptr_type)
378
379 uContext_t_ptr_type = gdb.lookup_type('UPP::uMachContext::uContext_t').pointer()
380
381 task_state = task['state']
382 if task_state == gdb.parse_and_eval('uBaseTask::Terminate'):
383 print('Cannot switch to a terminated thread')
384 return
385 task_context = task['context'].cast(uContext_t_ptr_type)
386
387 # lookup for sp,fp and uSwitch
388 xsp = task_context['SP'] + 48
389 xfp = task_context['FP']
390 if not gdb.lookup_symbol('uSwitch'):
391 print('uSwitch symbol is unavailable')
392 return
393
394 # convert string so we can strip out the address
395 xpc = get_addr(gdb.parse_and_eval('uSwitch').address + 28)
396 # must be at frame 0 to set pc register
397 gdb.execute('select-frame 0')
398
399 # push sp, fp, pc into a global stack
400 global STACK
401 sp = gdb.parse_and_eval('$sp')
402 fp = gdb.parse_and_eval('$fp')
403 pc = gdb.parse_and_eval('$pc')
404 stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
405 STACK.append(stack_info)
406
407 # update registers for new task
408 gdb.execute('set $rsp={}'.format(xsp))
409 gdb.execute('set $rbp={}'.format(xfp))
410 gdb.execute('set $pc={}'.format(xpc))
411
412 def pushtask_by_id(self, task_id, cluster_name):
413 """
414 @cluster_name: str
415 @task_id: str
416 """
417 try:
418 task_id = int(task_id)
419 except:
420 print_usage(self.usage_msg)
421 return
422
423 # retrieve the address associated with the cluster name
424 cluster_address = lookup_cluster_by_name(cluster_name)
425 if cluster_address == 0x0:
426 return
427
428 task_root = cluster_address.cast(uCluster_ptr_type)['tasksOnCluster']['root']
429 if task_root.address == 0x0:
430 print('There are no tasks on this cluster')
431 return
432
433 user_id = 0
434 task_addr = None
435 systask_id = -1 # system search id starts with negative
436
437 # lookup for the task associated with the id
438 global SysTask_Name
439 if (task_id >= 0 and cluster_name == "systemCluster"):
440 print('internal error: systemCluster does not have ID >= 0')
441 return
442 #id is a system task
443 elif task_id < 0:
444 curr = task_root
445 rootflag = False
446 while (curr['task_']['name'].string() not in SysTask_Name):
447 curr = curr['next'].cast(uBaseTaskDL_ptr_type)
448 if curr == task_root:
449 rootflag = True
450 break
451 if rootflag == False:
452 if task_id == systask_id:
453 task_addr = str(curr['task_'].address)
454 else:
455 while True:
456 curr = curr['next'].cast(uBaseTaskDL_ptr_type)
457
458 if (curr['task_']['name'].string() in SysTask_Name):
459 systask_id -= 1
460 if curr == task_root:
461 break
462 if task_id == systask_id:
463 task_addr = str(curr['task_'].address)
464 break
465
466 if curr == task_root:
467 break
468 #id is a user task
469 else:
470 curr = task_root
471 rootflag = False
472 while (curr['task_']['name'].string() in SysTask_Name):
473 curr = curr['next'].cast(uBaseTaskDL_ptr_type)
474 if curr == task_root:
475 rootflag = True
476 break
477 if rootflag == False:
478 if task_id == user_id:
479 task_addr = str(curr['task_'].address)
480 else:
481 while True:
482 curr = curr['next'].cast(uBaseTaskDL_ptr_type)
483
484 if (curr['task_']['name'].string() not in SysTask_Name):
485 user_id += 1
486 if curr == task_root:
487 break
488 if task_id == user_id:
489 task_addr = str(curr['task_'].address)
490 break
491
492 if curr == task_root:
493 break
494
495 if not task_addr:
496 print("Cannot find task ID: {}. Only have {} tasks".format(task_id,user_id))
497 else:
498 self.pushtask_by_address(task_addr)
499
500 def print_tasks_by_cluster_name(self, cluster_name):
501 """
502 Print out all the tasks available in the specified cluster
503 @cluster_name: str
504 """
505 cluster_address = lookup_cluster_by_name(cluster_name)
506 if cluster_address == 0x0:
507 return
508
509 self.print_tasks_by_cluster_all(cluster_address)
510
511 def invoke(self, arg, from_tty):
512 """
513 @arg: str
514 @from_tty: bool
515 """
516 argv = get_argv_list(arg)
517 if len(argv) == 0:
518 # print tasks
519 self.print_user_tasks() # only tasks and main
520 elif len(argv) == 1:
521 # push task
522 if argv[0].isdigit():
523 self.pushtask_by_id(argv[0], "userCluster") # by id, userCluster
524 elif argv[0].startswith('0x') or argv[0].startswith('0X'):
525 self.pushtask_by_address(argv[0]) # by address, any cluster
526 # print tasks
527 elif argv[0] == 'all':
528 self.print_all_tasks() # all tasks, all clusters
529 else:
530 self.print_tasks_by_cluster_name(argv[0]) # all tasks, specified cluster
531 elif len(argv) == 2:
532 # push task
533 self.pushtask_by_id(argv[0], argv[1]) # by id, specified cluster
534 else:
535 print('parse error')
536 print_usage(self.usage_msg)
537
538class PrevTask(gdb.Command):
539 """Switch back to previous task on the stack"""
540 usage_msg = 'prevtask <task_address>'
541
542 def __init__(self):
543 super(PrevTask, self).__init__('prevtask', gdb.COMMAND_USER)
544
545 def invoke(self, arg, from_tty):
546 """
547 @arg: str
548 @from_tty: bool
549 """
550 global STACK
551 if len(STACK) != 0:
552 # must be at frame 0 to set pc register
553 gdb.execute('select-frame 0')
554
555 # pop stack
556 stack_info = STACK.pop()
557 pc = get_addr(stack_info.pc)
558 sp = stack_info.sp
559 fp = stack_info.fp
560
561 # pop sp, fp, pc from global stack
562 gdb.execute('set $pc = {}'.format(pc))
563 gdb.execute('set $rbp = {}'.format(fp))
564 gdb.execute('set $sp = {}'.format(sp))
565
566 # must be at C++ frame to access C++ vars
567 gdb.execute('frame 1')
568 else:
569 print('empty stack')
570
571class ResetOriginFrame(gdb.Command):
572 """Reset to the origin frame prior to continue execution again"""
573 usage_msg = 'resetOriginFrame'
574 def __init__(self):
575 super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
576
577 def invoke(self, arg, from_tty):
578 """
579 @arg: str
580 @from_tty: bool
581 """
582 global STACK
583 if len(STACK) != 0:
584 stack_info = STACK.pop(0)
585 STACK.clear()
586 pc = get_addr(stack_info.pc)
587 sp = stack_info.sp
588 fp = stack_info.fp
589
590 # pop sp, fp, pc from global stack
591 gdb.execute('set $pc = {}'.format(pc))
592 gdb.execute('set $rbp = {}'.format(fp))
593 gdb.execute('set $sp = {}'.format(sp))
594
595 # must be at C++ frame to access C++ vars
596 gdb.execute('frame 1')
597 #else:
598 #print('reset: empty stack') #probably does not have to print msg
599
600Clusters()
601ClusterProcessors()
602PrevTask()
603ResetOriginFrame()
604Task()
Note: See TracBrowser for help on using the repository browser.