source: tools/gdb/utils-gdb.py@ 954c954

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

Fixed park unpark to support park as first step of main()
Fixes #170

  • Property mode set to 100644
File size: 15.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 all_clusters():
112 if not is_cforall():
113 return None
114
115 cluster_root = get_cluster_root()
116 if cluster_root.address == 0x0:
117 return
118
119 curr = cluster_root
120 ret = [curr]
121
122 while True:
123 curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
124 if curr == cluster_root:
125 break
126
127 ret.append(curr)
128
129 return ret
130
131
132def lookup_cluster(name = None):
133 """
134 Look up a cluster given its ID
135 @name: str
136 Return: gdb.Value
137 """
138 if not is_cforall():
139 return None
140
141 root = get_cluster_root()
142 if root.address == 0x0:
143 return None
144
145 if not name:
146 return root
147
148 # lookup for the task associated with the id
149 cluster = None
150 curr = root
151 while True:
152 if curr['_X4namePKc_1'].string() == name:
153 cluster = curr.address
154 break
155 curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
156 if curr == root or curr == 0x0:
157 break
158
159 if not cluster:
160 print("Cannot find a cluster with the name: {}.".format(name))
161 return None
162
163 return cluster
164
165def lookup_threads_by_cluster(cluster):
166 # Iterate through a circular linked list of threads and accumulate them in an array
167 threads = []
168
169 cfa_t = get_cfa_types()
170 root = cluster['_X7threadsS8__dllist_S7$thread__1']['_X4headPY15__TYPE_generic__1'].cast(cfa_t.thread_ptr)
171
172 if root == 0x0 or root.address == 0x0:
173 print('There are no tasks for cluster: {}'.format(cluster))
174 return threads
175
176 curr = root
177 tid = 0
178 sid = -1
179
180 while True:
181 t = ThreadInfo(cluster, curr)
182 if t.is_system():
183 t.tid = sid
184 sid -= 1
185 else:
186 t.tid = tid
187 tid += 1
188
189 threads.append(t)
190
191 curr = curr['node']['next']
192 if curr == root or curr == 0x0:
193 break
194
195 return threads
196
197def system_thread(thread):
198 return False
199
200def adjust_stack(pc, fp, sp):
201 # pop sp, fp, pc from global stack
202 gdb.execute('set $pc = {}'.format(pc))
203 gdb.execute('set $rbp = {}'.format(fp))
204 gdb.execute('set $sp = {}'.format(sp))
205
206############################ COMMAND IMPLEMENTATION #########################
207
208class Clusters(gdb.Command):
209 """Cforall: Display currently known clusters
210Usage:
211 info clusters : print out all the clusters
212"""
213
214 def __init__(self):
215 super(Clusters, self).__init__('info clusters', gdb.COMMAND_USER)
216
217 def print_cluster(self, cluster_name, cluster_address):
218 print('{:>20} {:>20}'.format(cluster_name, cluster_address))
219
220 #entry point from gdb
221 def invoke(self, arg, from_tty):
222 if not is_cforall():
223 return
224
225 if arg:
226 print("info clusters does not take arguments")
227 print_usage(self)
228 return
229
230 self.print_cluster('Name', 'Address')
231
232 for c in all_clusters():
233 self.print_cluster(c['_X4namePKc_1'].string(), str(c))
234
235 print("")
236
237############
238class Processors(gdb.Command):
239 """Cforall: Display currently known processors
240Usage:
241 info processors : print out all the processors in the Main Cluster
242 info processors all : print out all processors in all clusters
243 info processors <cluster_name> : print out all processors in a given cluster
244"""
245
246 def __init__(self):
247 super(Processors, self).__init__('info processors', gdb.COMMAND_USER)
248
249 def print_processor(self, name, status, pending, address):
250 print('{:>20} {:>11} {:>13} {:>20}'.format(name, status, pending, address))
251
252 def iterate_procs(self, root, active):
253 if root == 0x0:
254 return
255
256 cfa_t = get_cfa_types()
257 curr = root
258
259 while True:
260 processor = curr
261 should_stop = processor['_X12do_terminateVb_1']
262 stop_count = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
263 if not should_stop:
264 status = 'Active' if active else 'Idle'
265 else:
266 status_str = 'Last Thread' if stop_count >= 0 else 'Terminating'
267 status = '{}({},{})'.format(status_str, should_stop, stop_count)
268
269 self.print_processor(processor['_X4namePKc_1'].string(),
270 status, str(processor['_X18pending_preemptionb_1']), str(processor)
271 )
272
273 curr = curr['_X4nodeS28__processor____dbg_node_proc_1']['_X4nextPS9processor_1']
274
275 if curr == root or curr == 0x0:
276 break
277
278 #entry point from gdb
279 def invoke(self, arg, from_tty):
280 if not is_cforall():
281 return
282
283 if not arg:
284 clusters = [lookup_cluster(None)]
285 elif arg == "all":
286 clusters = all_clusters()
287 else:
288 clusters = [lookup_cluster(arg)]
289
290 if not clusters:
291 print("No Cluster matching arguments found")
292 return
293
294 cfa_t = get_cfa_types()
295 for cluster in clusters:
296 print('Cluster: "{}"({})'.format(cluster['_X4namePKc_1'].string(), cluster.cast(cfa_t.cluster_ptr)))
297
298 active_root = cluster.cast(cfa_t.cluster_ptr) \
299 ['_X5procsS8__dllist_S9processor__1'] \
300 ['_X4headPY15__TYPE_generic__1'] \
301 .cast(cfa_t.processor_ptr)
302
303 idle_root = cluster.cast(cfa_t.cluster_ptr) \
304 ['_X5idlesS8__dllist_S9processor__1'] \
305 ['_X4headPY15__TYPE_generic__1'] \
306 .cast(cfa_t.processor_ptr)
307
308 if idle_root != 0x0 or active_root != 0x0:
309 self.print_processor('Name', 'Status', 'Pending Yield', 'Address')
310 self.iterate_procs(active_root, True)
311 self.iterate_procs(idle_root, False)
312 else:
313 print("No processors on cluster")
314
315 print()
316
317############
318class Threads(gdb.Command):
319 """Cforall: Display currently known threads
320Usage:
321 cfathreads : print Main Cluster threads, application threads only
322 cfathreads all : print all clusters, all threads
323 cfathreads <clusterName> : print cluster threads, application threads only
324 """
325 def __init__(self):
326 # The first parameter of the line below is the name of the command. You
327 # can call it 'uc++ task'
328 super(Threads, self).__init__('info cfathreads', gdb.COMMAND_USER)
329
330 def print_formatted(self, marked, tid, name, state, address):
331 print('{:>1} {:>4} {:>20} {:>10} {:>20}'.format('*' if marked else ' ', tid, name, state, address))
332
333 def print_thread(self, thread, tid, marked):
334 cfa_t = get_cfa_types()
335 self.print_formatted(marked, tid, thread['self_cor']['name'].string(), str(thread['state'].cast(cfa_t.thread_state)), str(thread))
336
337 def print_threads_by_cluster(self, cluster, print_system = False):
338 # Iterate through a circular linked list of tasks and print out its
339 # name along with address associated to each cluster
340 threads = lookup_threads_by_cluster(cluster)
341 if not threads:
342 return
343
344 running_thread = find_curr_thread()
345 if running_thread is None:
346 print('Could not identify current thread')
347
348 self.print_formatted(False, '', 'Name', 'State', 'Address')
349
350 for t in threads:
351 if not t.is_system() or print_system:
352 self.print_thread(t.value, t.tid, t.value == running_thread if running_thread else False)
353
354 print()
355
356 def print_all_threads(self):
357 for c in all_clusters():
358 self.print_threads_by_cluster(c, False)
359
360 def invoke(self, arg, from_tty):
361 """
362 @arg: str
363 @from_tty: bool
364 """
365 if not is_cforall():
366 return
367
368 if not arg:
369 cluster = lookup_cluster()
370 if not cluster:
371 print("Could not find Main Cluster")
372 return
373
374 # only tasks and main
375 self.print_threads_by_cluster(cluster, False)
376
377 elif arg == 'all':
378 # all threads, all clusters
379 self.print_all_threads()
380
381 else:
382 cluster = lookup_cluster(arg)
383 if not cluster:
384 print("Could not find cluster '{}'".format(arg))
385 return
386
387 # all tasks, specified cluster
388 self.print_threads_by_cluster(cluster, True)
389
390
391############
392class Thread(gdb.Command):
393 """Cforall: Switch to specified user threads
394Usage:
395 cfathread <id> : switch stack to thread id on main cluster
396 cfathread 0x<address> : switch stack to thread on any cluster
397 cfathread <id> <clusterName> : switch stack to thread on specified cluster
398 """
399 def __init__(self):
400 # The first parameter of the line below is the name of the command. You
401 # can call it 'uc++ task'
402 super(Thread, self).__init__('cfathread', gdb.COMMAND_USER)
403
404 ############################ AUXILIARY FUNCTIONS #########################
405
406 def switchto(self, thread):
407 """Change to a new task by switching to a different stack and manually
408 adjusting sp, fp and pc
409 @task_address: str
410 2 supported format:
411 in hex format
412 <hex_address>: literal hexadecimal address
413 Ex: 0xffffff
414 in name of the pointer to the task
415 "task_name": pointer of the variable name of the cluster
416 Ex: T* s -> task_name = s
417 Return: gdb.value of the cluster's address
418 """
419 try:
420 if not gdb.lookup_symbol('__cfactx_switch'):
421 print('__cfactx_switch symbol is unavailable')
422 return
423 except:
424 print('here 3')
425
426 cfa_t = get_cfa_types()
427
428 state = thread['state'].cast(cfa_t.thread_state)
429 try:
430 if state == gdb.parse_and_eval('Halted'):
431 print('Cannot switch to a terminated thread')
432 return
433
434 if state == gdb.parse_and_eval('Start'):
435 print('Cannjot switch to a thread not yet run')
436 return
437 except:
438 print("here 2")
439 return
440
441
442 context = thread['context']
443
444 # lookup for sp,fp and uSwitch
445 xsp = context['SP'] + 48
446 xfp = context['FP']
447
448 # convert string so we can strip out the address
449 try:
450 xpc = get_addr(gdb.parse_and_eval('__cfactx_switch').address + 28)
451 except:
452 print("here")
453 return
454
455 # must be at frame 0 to set pc register
456 gdb.execute('select-frame 0')
457
458 # push sp, fp, pc into a global stack
459 global STACK
460 sp = gdb.parse_and_eval('$sp')
461 fp = gdb.parse_and_eval('$fp')
462 pc = gdb.parse_and_eval('$pc')
463 stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
464 STACK.append(stack_info)
465
466 # update registers for new task
467 print('switching to ')
468 gdb.execute('set $rsp={}'.format(xsp))
469 gdb.execute('set $rbp={}'.format(xfp))
470 gdb.execute('set $pc={}'.format(xpc))
471
472 def find_matching_gdb_thread_id():
473 """
474 Parse the str from info thread to get the number
475 """
476 info_thread_str = gdb.execute('info thread', to_string=True).splitlines()
477 for thread_str in info_thread_str:
478 if thread_str.find('this={}'.format(task)) != -1:
479 thread_id_pattern = r'^\*?\s+(\d+)\s+Thread'
480 # retrive gdb thread id
481 return re.match(thread_id_pattern, thread_str).group(1)
482
483 # check if the task is running or not
484 if task_state == gdb.parse_and_eval('uBaseTask::Running'):
485 # find the equivalent thread from info thread
486 gdb_thread_id = find_matching_gdb_thread_id()
487 if gdb_thread_id is None:
488 print('cannot find the thread id to switch to')
489 return
490 # switch to that thread based using thread command
491 gdb.execute('thread {}'.format(gdb_thread_id))
492
493 def switchto_id(self, tid, cluster):
494 """
495 @cluster: cluster object
496 @tid: int
497 """
498 threads = lookup_threads_by_cluster( cluster )
499
500 for t in threads:
501 if t.tid == tid:
502 self.switchto(t.value)
503 return
504
505 print("Cound not find thread by id '{}'".format(tid))
506
507 def invoke(self, arg, from_tty):
508 """
509 @arg: str
510 @from_tty: bool
511 """
512 if not is_cforall():
513 return
514
515 argv = parse(arg)
516 print(argv)
517 if argv[0].isdigit():
518 cname = " ".join(argv[1:]) if len(argv) > 1 else None
519 cluster = lookup_cluster(cname)
520 if not cluster:
521 print("Could not find cluster '{}'".format(cname if cname else "Main Cluster"))
522 return
523
524 try:
525 tid = int(argv[0])
526 except:
527 print("'{}' not a valid thread id".format(argv[0]))
528 print_usage(self)
529 return
530
531 # by id, userCluster
532 self.switchto_id(tid, cluster)
533
534 elif argv[0].startswith('0x') or argv[0].startswith('0X'):
535 self.switchto(argv[0]) # by address, any cluster
536
537############
538class PrevThread(gdb.Command):
539 """Switch back to previous task on the stack"""
540 usage_msg = 'prevtask'
541
542 def __init__(self):
543 super(PrevThread, 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 adjust_stack(pc, fp, sp)
563
564 # must be at C++ frame to access C++ vars
565 gdb.execute('frame 1')
566 else:
567 print('empty stack')
568
569class ResetOriginFrame(gdb.Command):
570 """Reset to the origin frame prior to continue execution again"""
571 usage_msg = 'resetOriginFrame'
572 def __init__(self):
573 super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
574
575 def invoke(self, arg, from_tty):
576 """
577 @arg: str
578 @from_tty: bool
579 """
580 global STACK
581 if len(STACK) != 0:
582 stack_info = STACK.pop(0)
583 STACK.clear()
584 pc = get_addr(stack_info.pc)
585 sp = stack_info.sp
586 fp = stack_info.fp
587
588 # pop sp, fp, pc from global stack
589 adjust_stack(pc, fp, sp)
590
591 # must be at C++ frame to access C++ vars
592 gdb.execute('frame 1')
593 #else:
594 #print('reset: empty stack') #probably does not have to print msg
595
596Clusters()
597Processors()
598ResetOriginFrame()
599PrevThread()
600Threads()
601Thread()
602
603# Local Variables: #
604# mode: Python #
605# End: #
Note: See TracBrowser for help on using the repository browser.