source: tools/gdb/utils-gdb.py@ 1cfdee9

ADT arm-eh ast-experimental enum forall-pointer-decay jacob/cs343-translation new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since 1cfdee9 was 1cfdee9, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

First draft changes to gdb support

  • Property mode set to 100644
File size: 22.5 KB
Line 
1#
2# Copyright (C) Lynn Tran, Jiachen Zhang 2018
3#
4# utils-gdb.py --
5#
6# Author : Lynn Tran
7# Created On : Mon Oct 1 22:06:09 2018
8# Last Modified By : Peter A. Buhr
9# Last Modified On : Sat Jan 19 14:16:10 2019
10# Update Count : 11
11#
12
13"""
14To run this extension, the python name has to be as same as one of the loaded library
15Additionally, the file must exist in a folder which is in gdb's safe path
16"""
17import collections
18import gdb
19import re
20
21# set these signal handlers with some settings (nostop, noprint, pass)
22gdb.execute('handle SIGALRM nostop noprint pass')
23gdb.execute('handle SIGUSR1 nostop noprint pass')
24
25CfaTypes = collections.namedtuple('CfaTypes', 'cluster_ptr processor_ptr thread_ptr int_ptr thread_state')
26
27class Thread:
28 tid = 0
29 cluster = None
30 value = None
31
32 def __init__(self, cluster, value):
33 self.cluster = cluster
34 self.value = value
35
36 def is_system(self):
37 return False
38
39# A named tuple representing information about a stack
40StackInfo = collections.namedtuple('StackInfo', 'sp fp pc')
41
42# A global variable to keep track of stack information as one switches from one
43# task to another task
44STACK = []
45
46# A global variable to keep all system task name
47SysTask_Name = ["uLocalDebuggerReader", "uLocalDebugger", "uProcessorTask", "uBootTask", "uSystemTask",
48"uProcessorTask", "uPthread", "uProfiler"]
49
50not_supported_error_msg = "Not a supported command for this language"
51
52def is_cforall():
53 return True
54
55def get_cfa_types():
56 # GDB types for various structures/types in CFA
57 return CfaTypes(cluster_ptr = gdb.lookup_type('struct cluster').pointer(),
58 processor_ptr = gdb.lookup_type('struct processor').pointer(),
59 thread_ptr = gdb.lookup_type('struct $thread').pointer(),
60 int_ptr = gdb.lookup_type('int').pointer(),
61 thread_state = gdb.lookup_type('enum coroutine_state'))
62
63def get_addr(addr):
64 """
65 NOTE: sketchy solution to retrieve address. There is a better solution...
66 @addr: str of an address that can be in a format 0xfffff <type of the object
67 at this address>
68 Return: str of just the address
69 """
70 str_addr = str(addr)
71 ending_addr_index = str_addr.find('<')
72 if ending_addr_index == -1:
73 return str(addr)
74 return str_addr[:ending_addr_index].strip()
75
76def print_usage(msg):
77 """
78 Print out usage message
79 @msg: str
80 """
81 print('Usage: ' + msg)
82
83def parse(args):
84 """
85 Split the argument list in string format, where each argument is separated
86 by whitespace delimiter, to a list of arguments like argv
87 @args: str of arguments
88 Return:
89 [] if args is an empty string
90 list if args is not empty
91 """
92 # parse the string format of arguments and return a list of arguments
93 argv = args.split(' ')
94 if len(argv) == 1 and argv[0] == '':
95 return []
96 return argv
97
98def get_cluster_root():
99 """
100 Return: gdb.Value of globalClusters.root (is an address)
101 """
102 cluster_root = gdb.parse_and_eval('_X11mainClusterPS7cluster_1')
103 if cluster_root.address == 0x0:
104 print('No clusters, program terminated')
105 return cluster_root
106
107def lookup_cluster(name = None):
108 """
109 Look up a cluster given its ID
110 @name: str
111 Return: gdb.Value
112 """
113 if not is_cforall():
114 return None
115
116 root = get_cluster_root()
117 if root.address == 0x0:
118 return None
119
120 if not name:
121 return root
122
123 # lookup for the task associated with the id
124 cluster = None
125 curr = root
126 while True:
127 if curr['_X4namePKc_1'].string() == name:
128 cluster = curr.address
129 break
130 curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
131 if curr == root or curr == 0x0:
132 break
133
134 if not cluster:
135 print("Cannot find a cluster with the name: {}.".format(name))
136 return None
137
138 return cluster
139
140def system_thread(thread):
141 return False
142
143def adjust_stack(pc, fp, sp):
144 # pop sp, fp, pc from global stack
145 gdb.execute('set $pc = {}'.format(pc))
146 gdb.execute('set $rbp = {}'.format(fp))
147 gdb.execute('set $sp = {}'.format(sp))
148
149############################ COMMAND IMPLEMENTATION #########################
150
151class Clusters(gdb.Command):
152 """Print out the list of available clusters"""
153 usage_msg = 'clusters'
154 str_format = ''
155
156 def __init__(self):
157 super(Clusters, self).__init__('clusters', gdb.COMMAND_USER)
158
159 def print_cluster(self, cluster_name, cluster_address):
160 print('{:>20} {:>20}'.format(cluster_name, cluster_address))
161
162 def invoke(self, arg, from_tty):
163 """
164 Iterate through a circular linked list of clusters and print out its
165 name along with address associated to each cluster
166 @arg: str
167 @from_tty: bool
168 """
169 if not is_cforall():
170 return
171
172 argv = parse(arg)
173 if len(argv) != 0:
174 print_usage('clusters')
175 return
176
177 cluster_root = get_cluster_root()
178 if cluster_root.address == 0x0:
179 return
180
181 curr = cluster_root
182 self.print_cluster('Name', 'Address')
183
184 while True:
185 self.print_cluster(curr['_X4namePKc_1'].string(), str(curr))
186 curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
187 if curr == cluster_root:
188 break
189
190 print("")
191
192############
193class Processors(gdb.Command):
194 """Display a list of all info about all available processors on a particular cluster"""
195 usage_msg = """
196 processors : print out all the processors in the Main Cluster
197 processors <cluster_name> : print out all processors in a given cluster"""
198
199 def __init__(self):
200 super(Processors, self).__init__('processors', gdb.COMMAND_USER)
201
202 def print_processor(self, name, status, pending, address):
203 print('{:>20} {:>20} {:>13} {:>20}'.format(name, status, pending, address))
204
205 def iterate_procs(self, root):
206 if root == 0x0:
207 print('{:>20}'.format("None"))
208 return
209
210 cfa_t = get_cfa_types()
211
212 self.print_processor('Name', 'Termination Status', 'Yield Pending', 'Address')
213 curr = root
214
215 while True:
216 processor = curr
217 should_stop = processor['_X12do_terminateVb_1']
218 stop_count = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
219 status_str = 'Running' if not should_stop else 'Last Thread' if stop_count >= 0 else 'Terminating'
220 status = '{}({},{})'.format(status_str, should_stop, stop_count)
221
222 self.print_processor(processor['_X4namePKc_1'].string(),
223 status, str(processor['_X18pending_preemptionb_1']), str(processor)
224 )
225
226 curr = curr['_X4nodeS28__processor____dbg_node_proc_1']['_X4nextPS9processor_1']
227
228 if curr == root or curr == 0x0:
229 break
230
231 print()
232
233 def invoke(self, arg, from_tty):
234 """
235 Iterate through a circular linked list of tasks and print out all
236 info about each processor in that cluster
237 @arg: str
238 @from_tty: bool
239 """
240 if not is_cforall():
241 return
242
243 cfa_t = get_cfa_types()
244
245 argv = parse(arg)
246 if len(argv) > 1:
247 print_usage(self.usage_msg)
248 return
249
250 cluster = lookup_cluster(argv[0] if len(argv) > 0 else None)
251
252 if cluster == 0x0 or cluster == None:
253 print("No Cluster matching arguments found")
254 return
255
256 print('Cluster {}({})'.format(cluster['_X4namePKc_1'].string(), cluster.cast(cfa_t.cluster_ptr)))
257
258 active_root = cluster.cast(cfa_t.cluster_ptr) \
259 ['_X5procsS8__dllist_S9processor__1'] \
260 ['_X4headPY15__TYPE_generic__1'] \
261 .cast(cfa_t.processor_ptr)
262
263 idle_root = cluster.cast(cfa_t.cluster_ptr) \
264 ['_X5idlesS8__dllist_S9processor__1'] \
265 ['_X4headPY15__TYPE_generic__1'] \
266 .cast(cfa_t.processor_ptr)
267
268 print("Active Processors")
269 self.iterate_procs(active_root)
270
271 print("\nIdle Processors")
272 self.iterate_procs(idle_root)
273
274############
275class Threads(gdb.Command):
276 def __init__(self):
277 # The first parameter of the line below is the name of the command. You
278 # can call it 'uc++ task'
279 super(Threads, self).__init__('cfathread', gdb.COMMAND_USER)
280
281 def print_usage(self):
282 print_usage("""
283 cfathread : print userCluster tasks, application tasks only
284 cfathread <clusterName> : print cluster tasks, application tasks only
285 cfathread all : print all clusters, all tasks
286 cfathread <id> : switch stack to thread id on userCluster
287 cfathread 0x<address> : switch stack to thread on any cluster
288 cfathread <id> <clusterName> : switch stack to thread on specified cluster
289 """)
290
291 ############################ AUXILIARY FUNCTIONS #########################
292
293 def print_formatted(self, marked, tid, name, state, address):
294 print('{:>1} {:>4} {:>20} {:>10} {:>20}'.format('*' if marked else ' ', tid, name, state, address))
295
296 def print_thread(self, thread, tid, marked):
297 cfa_t = get_cfa_types()
298 self.print_formatted(marked, tid, thread['self_cor']['name'].string(), str(thread['state'].cast(cfa_t.thread_state)), str(thread))
299
300 def print_formatted_cluster(self, str_format, cluster_name, cluster_addr):
301 print(str_format.format(cluster_name, cluster_addr))
302
303 def find_curr_thread(self):
304 # btstr = gdb.execute('bt', to_string = True).splitlines()
305 # if len(btstr) == 0:
306 # print('error')
307 # return None
308 # return btstr[0].split('this=',1)[1].split(',')[0].split(')')[0]
309 return None
310
311 def print_tasks_by_cluster_all(self, cluster_address):
312 """
313 Display a list of all info about all available tasks on a particular cluster
314 @cluster_address: gdb.Value
315 """
316 cluster_address = cluster_address.cast(uCPPTypes.ucluster_ptr)
317 task_root = cluster_address['tasksOnCluster']['root']
318
319 if task_root == 0x0 or task_root.address == 0x0:
320 print('There are no tasks for cluster at address: {}'.format(cluster_address))
321 return
322
323 self.print_formatted_task('', 'Task Name', 'Address', 'State')
324 curr = task_root
325 task_id = 0
326 systask_id = -1
327
328 breakpoint_addr = self.find_curr_breakpoint_addr()
329 if breakpoint_addr is None:
330 return
331
332 while True:
333 global SysTask_Name
334 if (curr['task_']['name'].string() in SysTask_Name):
335 self.print_formatted_tasks(systask_id, breakpoint_addr, curr)
336 systask_id -= 1
337 else:
338 self.print_formatted_tasks(task_id, breakpoint_addr, curr)
339 task_id += 1
340
341 curr = curr['next'].cast(uCPPTypes.uBaseTaskDL_ptr_type)
342 if curr == task_root:
343 break
344
345 def print_tasks_by_cluster_address_all(self, cluster_address):
346 """
347 Display a list of all info about all available tasks on a particular cluster
348 @cluster_address: str
349 """
350 # Iterate through a circular linked list of tasks and print out its
351 # name along with address associated to each cluster
352
353 # convert hex string to hex number
354 try:
355 hex_addr = int(cluster_address, 16)
356 except:
357 self.print_usage()
358 return
359
360 cluster_address = gdb.Value(hex_addr)
361 if not self.print_tasks_by_cluster_all(cluster_address):
362 return
363
364 def threads_by_cluster(self, cluster):
365 # Iterate through a circular linked list of threads and accumulate them in an array
366 threads = []
367
368 cfa_t = get_cfa_types()
369 root = cluster['_X7threadsS8__dllist_S7$thread__1']['_X4headPY15__TYPE_generic__1'].cast(cfa_t.thread_ptr)
370
371 if root == 0x0 or root.address == 0x0:
372 print('There are no tasks for cluster: {}'.format(cluster))
373 return threads
374
375 curr = root
376 tid = 0
377 sid = -1
378
379 while True:
380 t = Thread(cluster, curr)
381 if t.is_system():
382 t.tid = sid
383 sid -= 1
384 else:
385 t.tid = tid
386 tid += 1
387
388 threads.append(t)
389
390 curr = curr['node']['next']
391 if curr == root or curr == 0x0:
392 break
393
394 return threads
395
396 def print_threads_by_cluster(self, cluster, print_system = False):
397 """
398 Display a list of limited info about all available threads on a particular cluster
399 @cluster: str
400 @print_system: bool
401 """
402 # Iterate through a circular linked list of tasks and print out its
403 # name along with address associated to each cluster
404
405 threads = self.threads_by_cluster(cluster)
406 if not threads:
407 return
408
409 running_thread = self.find_curr_thread()
410 if running_thread is None:
411 print('Could not identify current thread')
412
413 self.print_formatted(False, '', 'Name', 'State', 'Address')
414
415 for t in threads:
416 if not t.is_system() or print_system:
417 self.print_thread(t.value, t.tid, tivalue == running_thread if running_thread else False)
418
419 print()
420
421 ############################ COMMAND FUNCTIONS #########################
422
423 def print_all_threads(self):
424 """Iterate through each cluster, iterate through all tasks and print out info about all the tasks
425 in those clusters"""
426 uCPPTypes = None
427 try:
428 uCPPTypes = get_uCPP_types()
429 except gdb.error:
430 print(not_supported_error_msg)
431 print(gdb.error)
432 return
433
434 cluster_root = get_cluster_root()
435 if cluster_root.address == 0x0:
436 return
437
438 curr = cluster_root
439 self.print_formatted_cluster(self.cluster_str_format, 'Cluster Name', 'Address')
440
441 while True:
442 addr = str(curr['cluster_'].reference_value())[1:]
443 self.print_formatted_cluster(self.cluster_str_format, curr['cluster_']['name'].string(), addr)
444
445 self.print_tasks_by_cluster_address_all(addr)
446 curr = curr['next'].cast(uCPPTypes.uClusterDL_ptr_type)
447 if curr == cluster_root:
448 break
449
450 def switchto(self, thread):
451 """Change to a new task by switching to a different stack and manually
452 adjusting sp, fp and pc
453 @task_address: str
454 2 supported format:
455 in hex format
456 <hex_address>: literal hexadecimal address
457 Ex: 0xffffff
458 in name of the pointer to the task
459 "task_name": pointer of the variable name of the cluster
460 Ex: T* s -> task_name = s
461 Return: gdb.value of the cluster's address
462 """
463 # uCPPTypes = None
464 # try:
465 # uCPPTypes = get_uCPP_types()
466 # except gdb.error:
467 # print(not_supported_error_msg)
468 # print(gdb.error)
469 # return
470
471 # # Task address has a format "task_address", which implies that it is the
472 # # name of the variable, and it needs to be evaluated
473 # if task_address.startswith('"') and task_address.endswith('"'):
474 # task = gdb.parse_and_eval(task_address.replace('"', ''))
475 # else:
476 # # Task address format does not include the quotation marks, which implies
477 # # that it is a hex address
478 # # convert hex string to hex number
479 # try:
480 # hex_addr = int(task_address, 16)
481 # except:
482 # self.print_usage()
483 # return
484 # task_address = gdb.Value(hex_addr)
485 # task = task_address.cast(uCPPTypes.uBaseTask_ptr_type)
486 try:
487 if not gdb.lookup_symbol('__cfactx_switch'):
488 print('__cfactx_switch symbol is unavailable')
489 return
490 except:
491 print('here 3')
492
493 cfa_t = get_cfa_types()
494
495 state = thread['state'].cast(cfa_t.thread_state)
496 try:
497 if state == gdb.parse_and_eval('Halted'):
498 print('Cannot switch to a terminated thread')
499 return
500
501 if state == gdb.parse_and_eval('Start'):
502 print('Cannjot switch to a thread not yet run')
503 return
504 except:
505 print("here 2")
506 return
507
508
509 context = thread['context']
510
511 # lookup for sp,fp and uSwitch
512 xsp = context['SP'] + 48
513 xfp = context['FP']
514
515 # convert string so we can strip out the address
516 try:
517 xpc = get_addr(gdb.parse_and_eval('__cfactx_switch').address + 28)
518 except:
519 print("here")
520 return
521
522 # must be at frame 0 to set pc register
523 gdb.execute('select-frame 0')
524
525 # push sp, fp, pc into a global stack
526 global STACK
527 sp = gdb.parse_and_eval('$sp')
528 fp = gdb.parse_and_eval('$fp')
529 pc = gdb.parse_and_eval('$pc')
530 stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
531 STACK.append(stack_info)
532
533 # update registers for new task
534 print('switching to ')
535 gdb.execute('set $rsp={}'.format(xsp))
536 gdb.execute('set $rbp={}'.format(xfp))
537 gdb.execute('set $pc={}'.format(xpc))
538
539 def find_matching_gdb_thread_id():
540 """
541 Parse the str from info thread to get the number
542 """
543 info_thread_str = gdb.execute('info thread', to_string=True).splitlines()
544 for thread_str in info_thread_str:
545 if thread_str.find('this={}'.format(task)) != -1:
546 thread_id_pattern = r'^\*?\s+(\d+)\s+Thread'
547 # retrive gdb thread id
548 return re.match(thread_id_pattern, thread_str).group(1)
549
550 # check if the task is running or not
551 if task_state == gdb.parse_and_eval('uBaseTask::Running'):
552 # find the equivalent thread from info thread
553 gdb_thread_id = find_matching_gdb_thread_id()
554 if gdb_thread_id is None:
555 print('cannot find the thread id to switch to')
556 return
557 # switch to that thread based using thread command
558 gdb.execute('thread {}'.format(gdb_thread_id))
559
560 def switchto_id(self, tid, cluster):
561 """
562 @cluster: cluster object
563 @tid: int
564 """
565 threads = self.threads_by_cluster( cluster )
566
567 for t in threads:
568 if t.tid == tid:
569 self.switchto(t.value)
570 return
571
572 print("Cound not find thread by id '{}'".format(tid))
573
574 def invoke(self, arg, from_tty):
575 """
576 @arg: str
577 @from_tty: bool
578 """
579 if not is_cforall():
580 return
581
582 argv = parse(arg)
583 print(argv)
584 if len(argv) == 0:
585 """
586 Iterate only Main Thread, print only tasks and main
587 """
588 cluster = lookup_cluster()
589 if not cluster:
590 print("Could not find Main Cluster")
591 return
592
593 # only tasks and main
594 self.print_threads_by_cluster(cluster, False)
595
596 elif len(argv) == 1:
597 if argv[0] == 'help':
598 self.print_usage()
599 # push task
600 elif argv[0].isdigit():
601 cluster = lookup_cluster()
602 if not cluster:
603 print("Could not find Main Cluster")
604 return
605
606 try:
607 tid = int(argv[0])
608 except:
609 print("'{}' not a valid thread id".format(argv[0]))
610 self.print_usage()
611 return
612
613 # by id, userCluster
614 self.switchto_id(tid, cluster)
615
616 elif argv[0].startswith('0x') or argv[0].startswith('0X'):
617 self.switchto(argv[0]) # by address, any cluster
618 # print tasks
619 elif argv[0] == 'all':
620 self.print_all_threads() # all tasks, all clusters
621 else:
622 """
623 Print out all the tasks available in the specified cluster
624 @cluster_name: str
625 """
626 print("cfathread by name")
627 cluster = lookup_cluster(argv[0])
628 if not cluster:
629 return
630
631 # all tasks, specified cluster
632 self.print_threads_by_cluster(cluster, True)
633
634 elif len(argv) == 2:
635 # push task
636 self.pushtask_by_id(argv[0], argv[1]) # by id, specified cluster
637 else:
638 print('Invalid arguments')
639 self.print_usage()
640
641############
642class PrevThread(gdb.Command):
643 """Switch back to previous task on the stack"""
644 usage_msg = 'prevtask'
645
646 def __init__(self):
647 super(PrevThread, self).__init__('prevtask', gdb.COMMAND_USER)
648
649 def invoke(self, arg, from_tty):
650 """
651 @arg: str
652 @from_tty: bool
653 """
654 global STACK
655 if len(STACK) != 0:
656 # must be at frame 0 to set pc register
657 gdb.execute('select-frame 0')
658
659 # pop stack
660 stack_info = STACK.pop()
661 pc = get_addr(stack_info.pc)
662 sp = stack_info.sp
663 fp = stack_info.fp
664
665 # pop sp, fp, pc from global stack
666 adjust_stack(pc, fp, sp)
667
668 # must be at C++ frame to access C++ vars
669 gdb.execute('frame 1')
670 else:
671 print('empty stack')
672
673class ResetOriginFrame(gdb.Command):
674 """Reset to the origin frame prior to continue execution again"""
675 usage_msg = 'resetOriginFrame'
676 def __init__(self):
677 super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
678
679 def invoke(self, arg, from_tty):
680 """
681 @arg: str
682 @from_tty: bool
683 """
684 global STACK
685 if len(STACK) != 0:
686 stack_info = STACK.pop(0)
687 STACK.clear()
688 pc = get_addr(stack_info.pc)
689 sp = stack_info.sp
690 fp = stack_info.fp
691
692 # pop sp, fp, pc from global stack
693 adjust_stack(pc, fp, sp)
694
695 # must be at C++ frame to access C++ vars
696 gdb.execute('frame 1')
697 #else:
698 #print('reset: empty stack') #probably does not have to print msg
699
700Clusters()
701Processors()
702ResetOriginFrame()
703PrevThread()
704Threads()
705
706# Local Variables: #
707# mode: Python #
708# End: #
Note: See TracBrowser for help on using the repository browser.