source: tools/gdb/utils-gdb.py@ df65c0c

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 df65c0c was d8b17e2, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

update gdb support after change to ready queue to use RWlock

  • Property mode set to 100644
File size: 16.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 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
46not_supported_error_msg = "Not a supported command for this language"
47
48def is_cforall():
49 return True
50
51def get_cfa_types():
52 # GDB types for various structures/types in CFA
53 return CfaTypes(cluster_ptr = gdb.lookup_type('struct cluster').pointer(),
54 processor_ptr = gdb.lookup_type('struct processor').pointer(),
55 thread_ptr = gdb.lookup_type('struct $thread').pointer(),
56 int_ptr = gdb.lookup_type('int').pointer(),
57 thread_state = gdb.lookup_type('enum __Coroutine_State'))
58
59def get_addr(addr):
60 """
61 NOTE: sketchy solution to retrieve address. There is a better solution...
62 @addr: str of an address that can be in a format 0xfffff <type of the object
63 at this address>
64 Return: str of just the address
65 """
66 str_addr = str(addr)
67 ending_addr_index = str_addr.find('<')
68 if ending_addr_index == -1:
69 return str(addr)
70 return str_addr[:ending_addr_index].strip()
71
72def print_usage(obj):
73 print(obj.__doc__)
74
75def parse(args):
76 """
77 Split the argument list in string format, where each argument is separated
78 by whitespace delimiter, to a list of arguments like argv
79 @args: str of arguments
80 Return:
81 [] if args is an empty string
82 list if args is not empty
83 """
84 # parse the string format of arguments and return a list of arguments
85 argv = args.split(' ')
86 if len(argv) == 1 and argv[0] == '':
87 return []
88 return argv
89
90def get_cluster_root():
91 """
92 Return: gdb.Value of globalClusters.root (is an address)
93 """
94 cluster_root = gdb.parse_and_eval('_X11mainClusterPS7cluster_1')
95 if cluster_root.address == 0x0:
96 print('No clusters, program terminated')
97 return cluster_root
98
99def get_sched_lock():
100 """
101 Return: gdb.Value of __scheduler_lock
102 """
103 lock = gdb.parse_and_eval('_X16__scheduler_lockPS20__scheduler_RWLock_t_1')
104 if lock.address == 0x0:
105 print('No scheduler lock, program terminated')
106 return lock
107
108def all_clusters():
109 if not is_cforall():
110 return None
111
112 cluster_root = get_cluster_root()
113 if cluster_root.address == 0x0:
114 return
115
116 curr = cluster_root
117 ret = [curr]
118
119 while True:
120 curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
121 if curr == cluster_root:
122 break
123
124 ret.append(curr)
125
126 return ret
127
128def all_processors():
129 if not is_cforall():
130 return None
131
132 cfa_t = get_cfa_types()
133
134 # get processors from registration to the RWlock
135 lock = get_sched_lock()
136
137 #get number of elements
138 count = lock['_X5readyVj_1']
139
140 #find all the procs
141 raw_procs = [lock['_X4dataPS21__scheduler_lock_id_t_1'][i]['_X6handleVPS16__processor_id_t_1'] for i in range(count)]
142
143 # pre cast full procs
144 procs = [p.cast(cfa_t.processor_ptr) for p in raw_procs if p['_X9full_procb_1']]
145
146 # sort procs by clusters
147 return sorted(procs, key=lambda p: p['_X4cltrPS7cluster_1'])
148
149def tls_for_pthread(pthrd):
150 prev = gdb.selected_thread()
151 inf = gdb.selected_inferior()
152
153 thrd = inf.thread_from_thread_handle( pthrd )
154 thrd.switch()
155 tls = gdb.parse_and_eval('&_X9kernelTLSS16KernelThreadData_1')
156
157 prev.switch()
158 return tls
159
160def tls_for_proc(proc):
161 return tls_for_pthread(proc['_X13kernel_threadm_1'])
162
163def thread_for_pthread(pthrd):
164 return tls_for_pthread(pthrd)['_X11this_threadVPS7$thread_1']
165
166def thread_for_proc(proc):
167 return tls_for_proc(proc)['_X11this_threadVPS7$thread_1']
168
169
170
171def find_curr_thread():
172 # btstr = gdb.execute('bt', to_string = True).splitlines()
173 # if len(btstr) == 0:
174 # print('error')
175 # return None
176 # return btstr[0].split('this=',1)[1].split(',')[0].split(')')[0]
177 return None
178
179def lookup_cluster(name = None):
180 """
181 Look up a cluster given its ID
182 @name: str
183 Return: gdb.Value
184 """
185 if not is_cforall():
186 return None
187
188 root = get_cluster_root()
189 if root.address == 0x0:
190 return None
191
192 if not name:
193 return root
194
195 # lookup for the task associated with the id
196 cluster = None
197 curr = root
198 while True:
199 if curr['_X4namePKc_1'].string() == name:
200 cluster = curr.address
201 break
202 curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
203 if curr == root or curr == 0x0:
204 break
205
206 if not cluster:
207 print("Cannot find a cluster with the name: {}.".format(name))
208 return None
209
210 return cluster
211
212def lookup_threads_by_cluster(cluster):
213 # Iterate through a circular linked list of threads and accumulate them in an array
214 threads = []
215
216 cfa_t = get_cfa_types()
217 root = cluster['_X7threadsS8__dllist_S7$thread__1']['_X4headPY15__TYPE_generic__1'].cast(cfa_t.thread_ptr)
218
219 if root == 0x0 or root.address == 0x0:
220 print('There are no tasks for cluster: {}'.format(cluster))
221 return threads
222
223 curr = root
224 tid = 0
225 sid = -1
226
227 while True:
228 t = ThreadInfo(cluster, curr)
229 if t.is_system():
230 t.tid = sid
231 sid -= 1
232 else:
233 t.tid = tid
234 tid += 1
235
236 threads.append(t)
237
238 curr = curr['node']['next']
239 if curr == root or curr == 0x0:
240 break
241
242 return threads
243
244def system_thread(thread):
245 return False
246
247def adjust_stack(pc, fp, sp):
248 # pop sp, fp, pc from global stack
249 gdb.execute('set $pc = {}'.format(pc))
250 gdb.execute('set $rbp = {}'.format(fp))
251 gdb.execute('set $sp = {}'.format(sp))
252
253############################ COMMAND IMPLEMENTATION #########################
254
255class Clusters(gdb.Command):
256 """Cforall: Display currently known clusters
257Usage:
258 info clusters : print out all the clusters
259"""
260
261 def __init__(self):
262 super(Clusters, self).__init__('info clusters', gdb.COMMAND_USER)
263
264 def print_cluster(self, cluster_name, cluster_address):
265 print('{:>20} {:>20}'.format(cluster_name, cluster_address))
266
267 #entry point from gdb
268 def invoke(self, arg, from_tty):
269 if not is_cforall():
270 return
271
272 if arg:
273 print("info clusters does not take arguments")
274 print_usage(self)
275 return
276
277 self.print_cluster('Name', 'Address')
278
279 for c in all_clusters():
280 self.print_cluster(c['_X4namePKc_1'].string(), str(c))
281
282 print("")
283
284############
285class Processors(gdb.Command):
286 """Cforall: Display currently known processors
287Usage:
288 info processors : print out all the processors
289 info processors <cluster_name> : print out all processors in a given cluster
290"""
291
292 def __init__(self):
293 super(Processors, self).__init__('info processors', gdb.COMMAND_USER)
294
295 def print_processor(self, processor):
296 should_stop = processor['_X12do_terminateVb_1']
297 if not should_stop:
298 midle = processor['_X6$linksS7$dlinks_S9processor__1']['_X4nextS9$mgd_link_Y13__tE_generic___1']['_X4elemPY13__tE_generic__1'] != 0x0
299 end = processor['_X6$linksS7$dlinks_S9processor__1']['_X4nextS9$mgd_link_Y13__tE_generic___1']['_X10terminatorPv_1'] != 0x0
300
301 status = 'Idle' if midle or end else 'Active'
302 else:
303 stop_count = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
304 status_str = 'Last Thread' if stop_count >= 0 else 'Terminating'
305 status = '{}({},{})'.format(status_str, should_stop, stop_count)
306
307 print('{:>20} {:>11} {:<7} {:<}'.format(
308 processor['_X4namePKc_1'].string(),
309 status,
310 str(processor['_X18pending_preemptionb_1']),
311 str(processor)
312 ))
313 tls = tls_for_proc( processor )
314 thrd = tls['_X11this_threadVPS7$thread_1']
315 if thrd != 0x0:
316 tname = '{} {}'.format(thrd['self_cor']['name'].string(), str(thrd))
317 else:
318 tname = None
319
320 print('{:>20} {}'.format('Thread', tname))
321 print('{:>20} {}'.format('TLS', tls))
322
323 #entry point from gdb
324 def invoke(self, arg, from_tty):
325 if not is_cforall():
326 return
327
328 if not arg:
329 clusters = all_clusters()
330 else:
331 clusters = [lookup_cluster(arg)]
332
333 if not clusters:
334 print("No Cluster matching arguments found")
335 return
336
337 procs = all_processors()
338
339 print('{:>20} {:>11} {:<7} {}'.format('Processor', '', 'Pending', 'Object'))
340 print('{:>20} {:>11} {:<7} {}'.format('Name', 'Status', 'Yield', 'Address'))
341 cl = None
342 for p in procs:
343 # if this is a different cluster print it
344 if cl != p['_X4cltrPS7cluster_1']:
345 if cl:
346 print()
347 cl = p['_X4cltrPS7cluster_1']
348 print('Cluster {}'.format(cl['_X4namePKc_1'].string()))
349
350 # print the processor information
351 self.print_processor(p)
352
353 print()
354
355############
356class Threads(gdb.Command):
357 """Cforall: Display currently known threads
358Usage:
359 cfathreads : print Main Cluster threads, application threads only
360 cfathreads all : print all clusters, all threads
361 cfathreads <clusterName> : print cluster threads, application threads only
362 """
363 def __init__(self):
364 # The first parameter of the line below is the name of the command. You
365 # can call it 'uc++ task'
366 super(Threads, self).__init__('info cfathreads', gdb.COMMAND_USER)
367
368 def print_formatted(self, marked, tid, name, state, address):
369 print('{:>1} {:>4} {:>20} {:>10} {:>20}'.format('*' if marked else ' ', tid, name, state, address))
370
371 def print_thread(self, thread, tid, marked):
372 cfa_t = get_cfa_types()
373 self.print_formatted(marked, tid, thread['self_cor']['name'].string(), str(thread['state'].cast(cfa_t.thread_state)), str(thread))
374
375 def print_threads_by_cluster(self, cluster, print_system = False):
376 # Iterate through a circular linked list of tasks and print out its
377 # name along with address associated to each cluster
378 threads = lookup_threads_by_cluster(cluster)
379 if not threads:
380 return
381
382 running_thread = find_curr_thread()
383 if running_thread is None:
384 print('Could not identify current thread')
385
386 self.print_formatted(False, '', 'Name', 'State', 'Address')
387
388 for t in threads:
389 if not t.is_system() or print_system:
390 self.print_thread(t.value, t.tid, t.value == running_thread if running_thread else False)
391
392 print()
393
394 def print_all_threads(self):
395 for c in all_clusters():
396 self.print_threads_by_cluster(c, False)
397
398 def invoke(self, arg, from_tty):
399 """
400 @arg: str
401 @from_tty: bool
402 """
403 if not is_cforall():
404 return
405
406 if not arg:
407 cluster = lookup_cluster()
408 if not cluster:
409 print("Could not find Main Cluster")
410 return
411
412 # only tasks and main
413 self.print_threads_by_cluster(cluster, False)
414
415 elif arg == 'all':
416 # all threads, all clusters
417 self.print_all_threads()
418
419 else:
420 cluster = lookup_cluster(arg)
421 if not cluster:
422 print("Could not find cluster '{}'".format(arg))
423 return
424
425 # all tasks, specified cluster
426 self.print_threads_by_cluster(cluster, True)
427
428
429############
430class Thread(gdb.Command):
431 """Cforall: Switch to specified user threads
432Usage:
433 cfathread <id> : switch stack to thread id on main cluster
434 cfathread 0x<address> : switch stack to thread on any cluster
435 cfathread <id> <clusterName> : switch stack to thread on specified cluster
436 """
437 def __init__(self):
438 # The first parameter of the line below is the name of the command. You
439 # can call it 'uc++ task'
440 super(Thread, self).__init__('cfathread', gdb.COMMAND_USER)
441
442 ############################ AUXILIARY FUNCTIONS #########################
443
444 def switchto(self, thread):
445 """Change to a new task by switching to a different stack and manually
446 adjusting sp, fp and pc
447 @task_address: str
448 2 supported format:
449 in hex format
450 <hex_address>: literal hexadecimal address
451 Ex: 0xffffff
452 in name of the pointer to the task
453 "task_name": pointer of the variable name of the cluster
454 Ex: T* s -> task_name = s
455 Return: gdb.value of the cluster's address
456 """
457 try:
458 if not gdb.lookup_symbol('__cfactx_switch'):
459 print('__cfactx_switch symbol is unavailable')
460 return
461 except:
462 print('here 3')
463
464 cfa_t = get_cfa_types()
465
466 state = thread['state'].cast(cfa_t.thread_state)
467 try:
468 if state == gdb.parse_and_eval('Halted'):
469 print('Cannot switch to a terminated thread')
470 return
471
472 if state == gdb.parse_and_eval('Start'):
473 print('Cannjot switch to a thread not yet run')
474 return
475 except:
476 print("here 2")
477 return
478
479
480 context = thread['context']
481
482 # lookup for sp,fp and uSwitch
483 xsp = context['SP'] + 48
484 xfp = context['FP']
485
486 # convert string so we can strip out the address
487 try:
488 xpc = get_addr(gdb.parse_and_eval('__cfactx_switch').address + 28)
489 except:
490 print("here")
491 return
492
493 # must be at frame 0 to set pc register
494 gdb.execute('select-frame 0')
495
496 # push sp, fp, pc into a global stack
497 global STACK
498 sp = gdb.parse_and_eval('$sp')
499 fp = gdb.parse_and_eval('$fp')
500 pc = gdb.parse_and_eval('$pc')
501 stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
502 STACK.append(stack_info)
503
504 # update registers for new task
505 print('switching to ')
506 gdb.execute('set $rsp={}'.format(xsp))
507 gdb.execute('set $rbp={}'.format(xfp))
508 gdb.execute('set $pc={}'.format(xpc))
509
510 def find_matching_gdb_thread_id():
511 """
512 Parse the str from info thread to get the number
513 """
514 info_thread_str = gdb.execute('info thread', to_string=True).splitlines()
515 for thread_str in info_thread_str:
516 if thread_str.find('this={}'.format(task)) != -1:
517 thread_id_pattern = r'^\*?\s+(\d+)\s+Thread'
518 # retrive gdb thread id
519 return re.match(thread_id_pattern, thread_str).group(1)
520
521 # check if the task is running or not
522 if task_state == gdb.parse_and_eval('uBaseTask::Running'):
523 # find the equivalent thread from info thread
524 gdb_thread_id = find_matching_gdb_thread_id()
525 if gdb_thread_id is None:
526 print('cannot find the thread id to switch to')
527 return
528 # switch to that thread based using thread command
529 gdb.execute('thread {}'.format(gdb_thread_id))
530
531 def switchto_id(self, tid, cluster):
532 """
533 @cluster: cluster object
534 @tid: int
535 """
536 threads = lookup_threads_by_cluster( cluster )
537
538 for t in threads:
539 if t.tid == tid:
540 self.switchto(t.value)
541 return
542
543 print("Cound not find thread by id '{}'".format(tid))
544
545 def invoke(self, arg, from_tty):
546 """
547 @arg: str
548 @from_tty: bool
549 """
550 if not is_cforall():
551 return
552
553 argv = parse(arg)
554 print(argv)
555 if argv[0].isdigit():
556 cname = " ".join(argv[1:]) if len(argv) > 1 else None
557 cluster = lookup_cluster(cname)
558 if not cluster:
559 print("Could not find cluster '{}'".format(cname if cname else "Main Cluster"))
560 return
561
562 try:
563 tid = int(argv[0])
564 except:
565 print("'{}' not a valid thread id".format(argv[0]))
566 print_usage(self)
567 return
568
569 # by id, userCluster
570 self.switchto_id(tid, cluster)
571
572 elif argv[0].startswith('0x') or argv[0].startswith('0X'):
573 self.switchto(argv[0]) # by address, any cluster
574
575############
576class PrevThread(gdb.Command):
577 """Switch back to previous task on the stack"""
578 usage_msg = 'prevtask'
579
580 def __init__(self):
581 super(PrevThread, self).__init__('prevtask', gdb.COMMAND_USER)
582
583 def invoke(self, arg, from_tty):
584 """
585 @arg: str
586 @from_tty: bool
587 """
588 global STACK
589 if len(STACK) != 0:
590 # must be at frame 0 to set pc register
591 gdb.execute('select-frame 0')
592
593 # pop stack
594 stack_info = STACK.pop()
595 pc = get_addr(stack_info.pc)
596 sp = stack_info.sp
597 fp = stack_info.fp
598
599 # pop sp, fp, pc from global stack
600 adjust_stack(pc, fp, sp)
601
602 # must be at C++ frame to access C++ vars
603 gdb.execute('frame 1')
604 else:
605 print('empty stack')
606
607class ResetOriginFrame(gdb.Command):
608 """Reset to the origin frame prior to continue execution again"""
609 usage_msg = 'resetOriginFrame'
610 def __init__(self):
611 super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
612
613 def invoke(self, arg, from_tty):
614 """
615 @arg: str
616 @from_tty: bool
617 """
618 global STACK
619 if len(STACK) != 0:
620 stack_info = STACK.pop(0)
621 STACK.clear()
622 pc = get_addr(stack_info.pc)
623 sp = stack_info.sp
624 fp = stack_info.fp
625
626 # pop sp, fp, pc from global stack
627 adjust_stack(pc, fp, sp)
628
629 # must be at C++ frame to access C++ vars
630 gdb.execute('frame 1')
631 #else:
632 #print('reset: empty stack') #probably does not have to print msg
633
634Clusters()
635Processors()
636ResetOriginFrame()
637PrevThread()
638Threads()
639Thread()
640
641# Local Variables: #
642# mode: Python #
643# End: #
Note: See TracBrowser for help on using the repository browser.