source: tools/gdb/utils-gdb.py@ 73530d9

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 73530d9 was 96df7c9c, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

Pushing intermediate to other machines

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