| 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 | """
 | 
|---|
| 14 | To run this extension, the python name has to be as same as one of the loaded library
 | 
|---|
| 15 | Additionally, the file must exist in a folder which is in gdb's safe path
 | 
|---|
| 16 | """
 | 
|---|
| 17 | import collections
 | 
|---|
| 18 | import gdb
 | 
|---|
| 19 | import re
 | 
|---|
| 20 | 
 | 
|---|
| 21 | # set these signal handlers with some settings (nostop, noprint, pass)
 | 
|---|
| 22 | gdb.execute('handle SIGALRM nostop noprint pass')
 | 
|---|
| 23 | gdb.execute('handle SIGUSR1 nostop noprint pass')
 | 
|---|
| 24 | 
 | 
|---|
| 25 | CfaTypes = collections.namedtuple('CfaTypes', 'cluster_ptr processor_ptr thread_ptr int_ptr uintptr thread_state yield_state')
 | 
|---|
| 26 | 
 | 
|---|
| 27 | class 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
 | 
|---|
| 40 | StackInfo = 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
 | 
|---|
| 44 | STACK = []
 | 
|---|
| 45 | 
 | 
|---|
| 46 | not_supported_error_msg = "Not a supported command for this language"
 | 
|---|
| 47 | 
 | 
|---|
| 48 | def is_cforall():
 | 
|---|
| 49 |         return True
 | 
|---|
| 50 | 
 | 
|---|
| 51 | def 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 |                 uintptr = gdb.lookup_type('uintptr_t'),
 | 
|---|
| 58 |                 thread_state = gdb.lookup_type('enum __Coroutine_State'),
 | 
|---|
| 59 |                 yield_state = gdb.lookup_type('enum __Preemption_Reason'))
 | 
|---|
| 60 | 
 | 
|---|
| 61 | def get_addr(addr):
 | 
|---|
| 62 |         """
 | 
|---|
| 63 |         NOTE: sketchy solution to retrieve address. There is a better solution...
 | 
|---|
| 64 |         @addr: str of an address that can be in a format 0xfffff <type of the object
 | 
|---|
| 65 |         at this address>
 | 
|---|
| 66 |         Return: str of just the address
 | 
|---|
| 67 |         """
 | 
|---|
| 68 |         str_addr = str(addr)
 | 
|---|
| 69 |         ending_addr_index = str_addr.find('<')
 | 
|---|
| 70 |         if ending_addr_index == -1:
 | 
|---|
| 71 |                 return str(addr)
 | 
|---|
| 72 |         return str_addr[:ending_addr_index].strip()
 | 
|---|
| 73 | 
 | 
|---|
| 74 | def print_usage(obj):
 | 
|---|
| 75 |         print(obj.__doc__)
 | 
|---|
| 76 | 
 | 
|---|
| 77 | def parse(args):
 | 
|---|
| 78 |         """
 | 
|---|
| 79 |         Split the argument list in string format, where each argument is separated
 | 
|---|
| 80 |         by whitespace delimiter, to a list of arguments like argv
 | 
|---|
| 81 |         @args: str of arguments
 | 
|---|
| 82 |         Return:
 | 
|---|
| 83 |                 [] if args is an empty string
 | 
|---|
| 84 |                 list if args is not empty
 | 
|---|
| 85 |         """
 | 
|---|
| 86 |         # parse the string format of arguments and return a list of arguments
 | 
|---|
| 87 |         argv = args.split(' ')
 | 
|---|
| 88 |         if len(argv) == 1 and argv[0] == '':
 | 
|---|
| 89 |                 return []
 | 
|---|
| 90 |         return argv
 | 
|---|
| 91 | 
 | 
|---|
| 92 | def single_field(obj):
 | 
|---|
| 93 |         """
 | 
|---|
| 94 |         If the struct only has one field return it, otherwise error
 | 
|---|
| 95 |         """
 | 
|---|
| 96 | 
 | 
|---|
| 97 |         _type = obj.type
 | 
|---|
| 98 |         if len(_type.fields()) != 1:
 | 
|---|
| 99 |                 return None
 | 
|---|
| 100 | 
 | 
|---|
| 101 |         return obj[_type.fields()[0].name]
 | 
|---|
| 102 | 
 | 
|---|
| 103 | 
 | 
|---|
| 104 | def start_from_dlist(dlist):
 | 
|---|
| 105 |         fs = dlist.type.fields()
 | 
|---|
| 106 |         if len(fs) != 1:
 | 
|---|
| 107 |                 print("Error, can't understand dlist type for", dlist, dlist.name, dlist.type)
 | 
|---|
| 108 |                 return None
 | 
|---|
| 109 | 
 | 
|---|
| 110 |         return dlist[fs[0]]
 | 
|---|
| 111 | 
 | 
|---|
| 112 | def fix_dlink(ptr):
 | 
|---|
| 113 |         """
 | 
|---|
| 114 |         Remove the higher order bit from the pointer
 | 
|---|
| 115 |         """
 | 
|---|
| 116 |         ptype = ptr.type
 | 
|---|
| 117 |         size = ptype.sizeof
 | 
|---|
| 118 |         if size == 8:
 | 
|---|
| 119 |                 bit = 1 << ((size*8)-1)
 | 
|---|
| 120 |                 mask = bit - 1
 | 
|---|
| 121 |         elif size == 4:
 | 
|---|
| 122 |                 bit = 0
 | 
|---|
| 123 |                 mask = 1
 | 
|---|
| 124 |         else:
 | 
|---|
| 125 |                 print("Unexpected pointer size while fixing dlink", size)
 | 
|---|
| 126 | 
 | 
|---|
| 127 |         cfa_t = get_cfa_types()
 | 
|---|
| 128 |         uptr = ptr.cast(cfa_t.uintptr)
 | 
|---|
| 129 |         return ptr if 0 == uptr & mask else gdb.Value(b'\x00'*size, ptype)
 | 
|---|
| 130 | 
 | 
|---|
| 131 | class ClusterIter:
 | 
|---|
| 132 |         def __init__(self, root):
 | 
|---|
| 133 |                 self.curr = None
 | 
|---|
| 134 |                 self.root = root
 | 
|---|
| 135 | 
 | 
|---|
| 136 |         def __iter__(self):
 | 
|---|
| 137 |                 return self
 | 
|---|
| 138 | 
 | 
|---|
| 139 |         def __next__(self):
 | 
|---|
| 140 |                 # Clusters form a cycle
 | 
|---|
| 141 |                 # If we haven't seen the root yet, then the root is the first
 | 
|---|
| 142 |                 if not self.curr:
 | 
|---|
| 143 |                         self.curr = self.root
 | 
|---|
| 144 |                         return self.curr
 | 
|---|
| 145 | 
 | 
|---|
| 146 |                 # if we already saw the root, then go forward
 | 
|---|
| 147 |                 self.curr = self.curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
 | 
|---|
| 148 | 
 | 
|---|
| 149 |                 # if we reached the root again, then we are done
 | 
|---|
| 150 |                 if self.curr == self.root:
 | 
|---|
| 151 |                         raise StopIteration
 | 
|---|
| 152 | 
 | 
|---|
| 153 |                 # otherwise return the next
 | 
|---|
| 154 |                 return self.curr
 | 
|---|
| 155 | 
 | 
|---|
| 156 | def all_clusters():
 | 
|---|
| 157 |         """
 | 
|---|
| 158 |         Return: a list of all the clusters as an iterator.
 | 
|---|
| 159 |         obtained from gdb.Value of globalClusters.root (is an address)
 | 
|---|
| 160 |         """
 | 
|---|
| 161 |         if not is_cforall():
 | 
|---|
| 162 |                 return []
 | 
|---|
| 163 | 
 | 
|---|
| 164 |         cluster_root = gdb.parse_and_eval('_X11mainClusterPS7cluster_1')
 | 
|---|
| 165 |         if cluster_root.address == 0x0:
 | 
|---|
| 166 |                 print('No clusters, program terminated')
 | 
|---|
| 167 |                 return []
 | 
|---|
| 168 | 
 | 
|---|
| 169 |         return ClusterIter(cluster_root)
 | 
|---|
| 170 | 
 | 
|---|
| 171 | class ProcIter:
 | 
|---|
| 172 |         def __init__(self, root):
 | 
|---|
| 173 |                 self.curr = None
 | 
|---|
| 174 |                 self.root = root
 | 
|---|
| 175 | 
 | 
|---|
| 176 |         def __iter__(self):
 | 
|---|
| 177 |                 return self
 | 
|---|
| 178 | 
 | 
|---|
| 179 |         def check(self):
 | 
|---|
| 180 |                 # check if this is the last value
 | 
|---|
| 181 |                 if not fix_dlink(self.curr):
 | 
|---|
| 182 |                         raise StopIteration
 | 
|---|
| 183 | 
 | 
|---|
| 184 |         def __next__(self):
 | 
|---|
| 185 |                 cfa_t = get_cfa_types()
 | 
|---|
| 186 | 
 | 
|---|
| 187 |                 # Processors form a cycle
 | 
|---|
| 188 |                 # If we haven't seen the root yet, then the root is the first
 | 
|---|
| 189 |                 if not self.curr:
 | 
|---|
| 190 |                         my_next = self.root
 | 
|---|
| 191 |                         self.curr = my_next.cast(cfa_t.processor_ptr)
 | 
|---|
| 192 | 
 | 
|---|
| 193 |                         #check if this is an empty list
 | 
|---|
| 194 |                         self.check()
 | 
|---|
| 195 | 
 | 
|---|
| 196 |                         return self.curr
 | 
|---|
| 197 | 
 | 
|---|
| 198 |                 # if we already saw the root, then go forward
 | 
|---|
| 199 |                 my_next = self.curr['_X4linkS5dlink_S9processor__1']['_X4nextPY13__tE_generic__1']
 | 
|---|
| 200 |                 self.curr = my_next.cast(cfa_t.processor_ptr)
 | 
|---|
| 201 | 
 | 
|---|
| 202 |                 #check if we reached the end
 | 
|---|
| 203 |                 self.check()
 | 
|---|
| 204 | 
 | 
|---|
| 205 |                 # otherwise return the next
 | 
|---|
| 206 |                 return self.curr
 | 
|---|
| 207 | 
 | 
|---|
| 208 | def proc_list(cluster):
 | 
|---|
| 209 |         """
 | 
|---|
| 210 |         Return: for a given processor, return the active and idle processors, as 2 iterators
 | 
|---|
| 211 |         """
 | 
|---|
| 212 |         cfa_t = get_cfa_types()
 | 
|---|
| 213 |         proclist = cluster['_X5procsS19__cluster_proc_list_1']
 | 
|---|
| 214 | 
 | 
|---|
| 215 |         idle = start_from_dlist(proclist['_X5idlesS5dlist_S9processorS5dlink_S9processor___1'])['_X4nextPY13__tE_generic__1']
 | 
|---|
| 216 |         active = start_from_dlist(proclist['_X7activesS5dlist_S9processorS5dlink_S9processor___1'])['_X4nextPY13__tE_generic__1']
 | 
|---|
| 217 |         return ProcIter(active.cast(cfa_t.processor_ptr)), ProcIter(idle.cast(cfa_t.processor_ptr))
 | 
|---|
| 218 | 
 | 
|---|
| 219 | def all_processors():
 | 
|---|
| 220 |         procs = []
 | 
|---|
| 221 |         for c in all_clusters():
 | 
|---|
| 222 |                 active, idle = proc_list(c)
 | 
|---|
| 223 |                 for p in active:
 | 
|---|
| 224 |                         procs.append(p)
 | 
|---|
| 225 | 
 | 
|---|
| 226 |                 for p in idle:
 | 
|---|
| 227 |                         procs.append(p)
 | 
|---|
| 228 | 
 | 
|---|
| 229 |         print(procs)
 | 
|---|
| 230 |         return procs
 | 
|---|
| 231 | 
 | 
|---|
| 232 | def tls_for_pthread(pthrd):
 | 
|---|
| 233 |         prev = gdb.selected_thread()
 | 
|---|
| 234 |         inf = gdb.selected_inferior()
 | 
|---|
| 235 | 
 | 
|---|
| 236 |         thrd = inf.thread_from_thread_handle( pthrd )
 | 
|---|
| 237 |         thrd.switch()
 | 
|---|
| 238 |         tls = gdb.parse_and_eval('&_X9kernelTLSS16KernelThreadData_1')
 | 
|---|
| 239 | 
 | 
|---|
| 240 |         prev.switch()
 | 
|---|
| 241 |         return tls
 | 
|---|
| 242 | 
 | 
|---|
| 243 | def tls_for_proc(proc):
 | 
|---|
| 244 |         return proc['_X10local_dataPS16KernelThreadData_1']
 | 
|---|
| 245 | 
 | 
|---|
| 246 | def thread_for_pthread(pthrd):
 | 
|---|
| 247 |         return tls_for_pthread(pthrd)['_X11this_threadVPS7thread$_1']
 | 
|---|
| 248 | 
 | 
|---|
| 249 | def thread_for_proc(proc):
 | 
|---|
| 250 |         return tls_for_proc(proc)['_X11this_threadVPS7thread$_1']
 | 
|---|
| 251 | 
 | 
|---|
| 252 | 
 | 
|---|
| 253 | 
 | 
|---|
| 254 | def find_curr_thread():
 | 
|---|
| 255 |         # btstr = gdb.execute('bt', to_string = True).splitlines()
 | 
|---|
| 256 |         # if len(btstr) == 0:
 | 
|---|
| 257 |         #     print('error')
 | 
|---|
| 258 |         #     return None
 | 
|---|
| 259 |         # return btstr[0].split('this=',1)[1].split(',')[0].split(')')[0]
 | 
|---|
| 260 |         return None
 | 
|---|
| 261 | 
 | 
|---|
| 262 | def lookup_cluster(name = None):
 | 
|---|
| 263 |         """
 | 
|---|
| 264 |         Look up one or more cluster given a name
 | 
|---|
| 265 |         @name: str
 | 
|---|
| 266 |         Return: gdb.Value
 | 
|---|
| 267 |         """
 | 
|---|
| 268 |         if not is_cforall():
 | 
|---|
| 269 |                 return None
 | 
|---|
| 270 | 
 | 
|---|
| 271 |         clusters = all_clusters()
 | 
|---|
| 272 |         if not clusters:
 | 
|---|
| 273 |                 return None
 | 
|---|
| 274 | 
 | 
|---|
| 275 |         if not name:
 | 
|---|
| 276 |                 return clusters.root
 | 
|---|
| 277 | 
 | 
|---|
| 278 |         # lookup for the task associated with the id
 | 
|---|
| 279 |         found = [c for c in clusters if c['_X4namePKc_1'].string() == name]
 | 
|---|
| 280 | 
 | 
|---|
| 281 |         if not found:
 | 
|---|
| 282 |                 print("Cannot find a cluster with the name: {}.".format(name))
 | 
|---|
| 283 |                 return None
 | 
|---|
| 284 | 
 | 
|---|
| 285 |         return found
 | 
|---|
| 286 | 
 | 
|---|
| 287 | 
 | 
|---|
| 288 | def lookup_threads_by_cluster(cluster):
 | 
|---|
| 289 |                 # Iterate through a circular linked list of threads and accumulate them in an array
 | 
|---|
| 290 |                 threads = []
 | 
|---|
| 291 | 
 | 
|---|
| 292 |                 cfa_t = get_cfa_types()
 | 
|---|
| 293 |                 head = single_field(cluster['_X7threadsS5dlist_S7thread$S18__thread_user_link__1'])
 | 
|---|
| 294 |                 root = head['_X4nextPY13__tE_generic__1'].cast(cfa_t.thread_ptr)
 | 
|---|
| 295 | 
 | 
|---|
| 296 |                 if root == 0x0 or root.address == 0x0:
 | 
|---|
| 297 |                         print('There are no tasks for cluster: {}'.format(cluster))
 | 
|---|
| 298 |                         return threads
 | 
|---|
| 299 | 
 | 
|---|
| 300 |                 curr = root
 | 
|---|
| 301 |                 tid = 0
 | 
|---|
| 302 |                 sid = -1
 | 
|---|
| 303 | 
 | 
|---|
| 304 |                 while True:
 | 
|---|
| 305 |                         t = ThreadInfo(cluster, curr)
 | 
|---|
| 306 |                         if t.is_system():
 | 
|---|
| 307 |                                 t.tid = sid
 | 
|---|
| 308 |                                 sid -= 1
 | 
|---|
| 309 |                         else:
 | 
|---|
| 310 |                                 t.tid = tid
 | 
|---|
| 311 |                                 tid += 1
 | 
|---|
| 312 | 
 | 
|---|
| 313 |                         threads.append(t)
 | 
|---|
| 314 | 
 | 
|---|
| 315 |                         curr = fix_dlink(single_field(curr['cltr_link'])['_X4nextPY13__tE_generic__1']).cast(cfa_t.thread_ptr)
 | 
|---|
| 316 |                         if curr == root or curr == 0x0:
 | 
|---|
| 317 |                                 break
 | 
|---|
| 318 | 
 | 
|---|
| 319 |                 return threads
 | 
|---|
| 320 | 
 | 
|---|
| 321 | def system_thread(thread):
 | 
|---|
| 322 |         return False
 | 
|---|
| 323 | 
 | 
|---|
| 324 | def adjust_stack(pc, fp, sp):
 | 
|---|
| 325 |         # pop sp, fp, pc from global stack
 | 
|---|
| 326 |         gdb.execute('set $pc = {}'.format(pc))
 | 
|---|
| 327 |         gdb.execute('set $rbp = {}'.format(fp))
 | 
|---|
| 328 |         gdb.execute('set $sp = {}'.format(sp))
 | 
|---|
| 329 | 
 | 
|---|
| 330 | ############################ COMMAND IMPLEMENTATION #########################
 | 
|---|
| 331 | 
 | 
|---|
| 332 | class Clusters(gdb.Command):
 | 
|---|
| 333 |         """Cforall: Display currently known clusters
 | 
|---|
| 334 | Usage:
 | 
|---|
| 335 |         info clusters                 : print out all the clusters
 | 
|---|
| 336 | """
 | 
|---|
| 337 | 
 | 
|---|
| 338 |         def __init__(self):
 | 
|---|
| 339 |                 super(Clusters, self).__init__('info clusters', gdb.COMMAND_USER)
 | 
|---|
| 340 | 
 | 
|---|
| 341 |         def print_cluster(self, cluster_name, cluster_address):
 | 
|---|
| 342 |                 print('{:>20}  {:>20}'.format(cluster_name, cluster_address))
 | 
|---|
| 343 | 
 | 
|---|
| 344 |         #entry point from gdb
 | 
|---|
| 345 |         def invoke(self, arg, from_tty):
 | 
|---|
| 346 |                 if not is_cforall():
 | 
|---|
| 347 |                         return
 | 
|---|
| 348 | 
 | 
|---|
| 349 |                 if arg:
 | 
|---|
| 350 |                         print("info clusters does not take arguments")
 | 
|---|
| 351 |                         print_usage(self)
 | 
|---|
| 352 |                         return
 | 
|---|
| 353 | 
 | 
|---|
| 354 |                 self.print_cluster('Name', 'Address')
 | 
|---|
| 355 | 
 | 
|---|
| 356 |                 for c in all_clusters():
 | 
|---|
| 357 |                         self.print_cluster(c['_X4namePKc_1'].string(), str(c))
 | 
|---|
| 358 | 
 | 
|---|
| 359 |                 print("")
 | 
|---|
| 360 | 
 | 
|---|
| 361 | ############
 | 
|---|
| 362 | class Processors(gdb.Command):
 | 
|---|
| 363 |         """Cforall: Display currently known processors
 | 
|---|
| 364 | Usage:
 | 
|---|
| 365 |         info processors                 : print out all the processors
 | 
|---|
| 366 |         info processors <cluster_name>  : print out all processors in a given cluster
 | 
|---|
| 367 | """
 | 
|---|
| 368 | 
 | 
|---|
| 369 |         def __init__(self):
 | 
|---|
| 370 |                 super(Processors, self).__init__('info processors', gdb.COMMAND_USER)
 | 
|---|
| 371 | 
 | 
|---|
| 372 |         def print_processor(self, processor, in_stats):
 | 
|---|
| 373 |                 should_stop = processor['_X12do_terminateVb_1']
 | 
|---|
| 374 |                 if not should_stop:
 | 
|---|
| 375 |                         status = in_stats
 | 
|---|
| 376 |                 else:
 | 
|---|
| 377 |                         stop_count  = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
 | 
|---|
| 378 |                         status_str  = 'Last Thread' if stop_count >= 0 else 'Terminating'
 | 
|---|
| 379 |                         status      = '{}({},{})'.format(status_str, should_stop, stop_count)
 | 
|---|
| 380 | 
 | 
|---|
| 381 |                 print('{:>20}  {:>11}  {:<7}  {:<}'.format(
 | 
|---|
| 382 |                         processor['_X4namePKc_1'].string(),
 | 
|---|
| 383 |                         status,
 | 
|---|
| 384 |                         str(processor['_X18pending_preemptionb_1']),
 | 
|---|
| 385 |                         str(processor)
 | 
|---|
| 386 |                 ))
 | 
|---|
| 387 |                 tls = tls_for_proc( processor )
 | 
|---|
| 388 |                 thrd = thread_for_proc( processor )
 | 
|---|
| 389 |                 if thrd != 0x0:
 | 
|---|
| 390 |                         tname = '{} {}'.format(thrd['self_cor']['name'].string(), str(thrd))
 | 
|---|
| 391 |                 else:
 | 
|---|
| 392 |                         tname = None
 | 
|---|
| 393 | 
 | 
|---|
| 394 |                 print('{:>20}  {}'.format('Thread', tname))
 | 
|---|
| 395 |                 print('{:>20}  {}'.format('TLS', tls))
 | 
|---|
| 396 | 
 | 
|---|
| 397 |         #entry point from gdb
 | 
|---|
| 398 |         def invoke(self, arg, from_tty):
 | 
|---|
| 399 |                 if not is_cforall():
 | 
|---|
| 400 |                         return
 | 
|---|
| 401 | 
 | 
|---|
| 402 |                 if not arg:
 | 
|---|
| 403 |                         clusters = all_clusters()
 | 
|---|
| 404 |                 else:
 | 
|---|
| 405 |                         clusters = [lookup_cluster(arg)]
 | 
|---|
| 406 | 
 | 
|---|
| 407 |                 if not clusters:
 | 
|---|
| 408 |                         print("No Cluster matching arguments found")
 | 
|---|
| 409 |                         return
 | 
|---|
| 410 | 
 | 
|---|
| 411 |                 print('{:>20}  {:>11}  {:<7}  {}'.format('Processor', '', 'Pending', 'Object'))
 | 
|---|
| 412 |                 print('{:>20}  {:>11}  {:<7}  {}'.format('Name', 'Status', 'Yield', 'Address'))
 | 
|---|
| 413 |                 for c in clusters:
 | 
|---|
| 414 |                         print('Cluster {}'.format(c['_X4namePKc_1'].string()))
 | 
|---|
| 415 | 
 | 
|---|
| 416 |                         active, idle = proc_list(c)
 | 
|---|
| 417 |                         # print the processor information
 | 
|---|
| 418 |                         for p in active:
 | 
|---|
| 419 |                                 self.print_processor(p, 'Active')
 | 
|---|
| 420 | 
 | 
|---|
| 421 |                         for p in idle:
 | 
|---|
| 422 |                                 self.print_processor(p, 'Idle')
 | 
|---|
| 423 | 
 | 
|---|
| 424 |                         print()
 | 
|---|
| 425 | 
 | 
|---|
| 426 |                 print()
 | 
|---|
| 427 | 
 | 
|---|
| 428 | ############
 | 
|---|
| 429 | class Threads(gdb.Command):
 | 
|---|
| 430 |         """Cforall: Display currently known threads
 | 
|---|
| 431 | Usage:
 | 
|---|
| 432 |         cfathreads                           : print Main Cluster threads, application threads only
 | 
|---|
| 433 |         cfathreads all                       : print all clusters, all threads
 | 
|---|
| 434 |         cfathreads <clusterName>             : print cluster threads, application threads only
 | 
|---|
| 435 |         """
 | 
|---|
| 436 |         def __init__(self):
 | 
|---|
| 437 |                 # The first parameter of the line below is the name of the command. You
 | 
|---|
| 438 |                 # can call it 'uc++ task'
 | 
|---|
| 439 |                 super(Threads, self).__init__('info cfathreads', gdb.COMMAND_USER)
 | 
|---|
| 440 | 
 | 
|---|
| 441 |         def print_formatted(self, marked, tid, name, state, address):
 | 
|---|
| 442 |                 # print(marked, tid, name, state, address)
 | 
|---|
| 443 |                 print('{:>1}  {:>4}  {:>20}  {:>10}  {:>20}'.format('*' if marked else ' ', tid, name, state, address))
 | 
|---|
| 444 | 
 | 
|---|
| 445 |         def print_thread(self, thread, tid, marked):
 | 
|---|
| 446 |                 # print("print", thread, tid, marked)
 | 
|---|
| 447 |                 cfa_t = get_cfa_types()
 | 
|---|
| 448 |                 ys = str(thread['preempted'].cast(cfa_t.yield_state))
 | 
|---|
| 449 |                 if ys == '_X15__NO_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
| 450 |                         state = str(thread['state'].cast(cfa_t.thread_state))
 | 
|---|
| 451 |                 elif ys == '_X18__ALARM_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
| 452 |                         state = 'preempted'
 | 
|---|
| 453 |                 elif ys == '_X19__MANUAL_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
| 454 |                         state = 'yield'
 | 
|---|
| 455 |                 elif ys == '_X17__POLL_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
| 456 |                         state = 'poll'
 | 
|---|
| 457 |                 else:
 | 
|---|
| 458 |                         print("error: thread {} in undefined preemption state {}".format(thread, ys))
 | 
|---|
| 459 |                         state = 'error'
 | 
|---|
| 460 |                 self.print_formatted(marked, tid, thread['self_cor']['name'].string(), state, str(thread))
 | 
|---|
| 461 | 
 | 
|---|
| 462 |         def print_threads_by_cluster(self, cluster, print_system = False):
 | 
|---|
| 463 |                 # Iterate through a circular linked list of tasks and print out its
 | 
|---|
| 464 |                 # name along with address associated to each cluster
 | 
|---|
| 465 |                 threads = lookup_threads_by_cluster(cluster)
 | 
|---|
| 466 |                 if not threads:
 | 
|---|
| 467 |                         return
 | 
|---|
| 468 | 
 | 
|---|
| 469 |                 running_thread = find_curr_thread()
 | 
|---|
| 470 |                 if running_thread is None:
 | 
|---|
| 471 |                         print('Could not identify current thread')
 | 
|---|
| 472 | 
 | 
|---|
| 473 |                 self.print_formatted(False, '', 'Name', 'State', 'Address')
 | 
|---|
| 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 |         def print_all_threads(self):
 | 
|---|
| 481 |                 for c in all_clusters():
 | 
|---|
| 482 |                         self.print_threads_by_cluster(c, False)
 | 
|---|
| 483 | 
 | 
|---|
| 484 |         def invoke(self, arg, from_tty):
 | 
|---|
| 485 |                 """
 | 
|---|
| 486 |                 @arg: str
 | 
|---|
| 487 |                 @from_tty: bool
 | 
|---|
| 488 |                 """
 | 
|---|
| 489 |                 if not is_cforall():
 | 
|---|
| 490 |                         return
 | 
|---|
| 491 | 
 | 
|---|
| 492 |                 if not arg:
 | 
|---|
| 493 |                         cluster = lookup_cluster()
 | 
|---|
| 494 |                         if not cluster:
 | 
|---|
| 495 |                                 print("Could not find Main Cluster")
 | 
|---|
| 496 |                                 return
 | 
|---|
| 497 | 
 | 
|---|
| 498 |                         # only tasks and main
 | 
|---|
| 499 |                         self.print_threads_by_cluster(cluster, False)
 | 
|---|
| 500 | 
 | 
|---|
| 501 |                 elif arg == 'all':
 | 
|---|
| 502 |                         # all threads, all clusters
 | 
|---|
| 503 |                         self.print_all_threads()
 | 
|---|
| 504 | 
 | 
|---|
| 505 |                 else:
 | 
|---|
| 506 |                         cluster = lookup_cluster(arg)
 | 
|---|
| 507 |                         if not cluster:
 | 
|---|
| 508 |                                 print("No matching cluster")
 | 
|---|
| 509 |                                 return
 | 
|---|
| 510 | 
 | 
|---|
| 511 |                         # all tasks, specified cluster
 | 
|---|
| 512 |                         self.print_threads_by_cluster(cluster, True)
 | 
|---|
| 513 | 
 | 
|---|
| 514 | 
 | 
|---|
| 515 | ############
 | 
|---|
| 516 | class Thread(gdb.Command):
 | 
|---|
| 517 |         """Cforall: Switch to specified user threads
 | 
|---|
| 518 | Usage:
 | 
|---|
| 519 |         cfathread <id>                       : switch stack to thread id on main cluster
 | 
|---|
| 520 |         cfathread 0x<address>                : switch stack to thread on any cluster
 | 
|---|
| 521 |         cfathread <id> <clusterName>         : switch stack to thread on specified cluster
 | 
|---|
| 522 |         """
 | 
|---|
| 523 |         def __init__(self):
 | 
|---|
| 524 |                 # The first parameter of the line below is the name of the command. You
 | 
|---|
| 525 |                 # can call it 'uc++ task'
 | 
|---|
| 526 |                 super(Thread, self).__init__('cfathread', gdb.COMMAND_USER)
 | 
|---|
| 527 | 
 | 
|---|
| 528 |         ############################ AUXILIARY FUNCTIONS #########################
 | 
|---|
| 529 | 
 | 
|---|
| 530 |         def switchto(self, thread):
 | 
|---|
| 531 |                 """Change to a new task by switching to a different stack and manually
 | 
|---|
| 532 |                 adjusting sp, fp and pc
 | 
|---|
| 533 |                 @task_address: str
 | 
|---|
| 534 |                         2 supported format:
 | 
|---|
| 535 |                                 in hex format
 | 
|---|
| 536 |                                         <hex_address>: literal hexadecimal address
 | 
|---|
| 537 |                                         Ex: 0xffffff
 | 
|---|
| 538 |                                 in name of the pointer to the task
 | 
|---|
| 539 |                                         "task_name": pointer of the variable name of the cluster
 | 
|---|
| 540 |                                                 Ex: T* s -> task_name = s
 | 
|---|
| 541 |                         Return: gdb.value of the cluster's address
 | 
|---|
| 542 |                 """
 | 
|---|
| 543 |                 try:
 | 
|---|
| 544 |                         if not gdb.lookup_symbol('__cfactx_switch'):
 | 
|---|
| 545 |                                 print('__cfactx_switch symbol is unavailable')
 | 
|---|
| 546 |                                 return
 | 
|---|
| 547 |                 except:
 | 
|---|
| 548 |                         print('here 3')
 | 
|---|
| 549 | 
 | 
|---|
| 550 |                 cfa_t = get_cfa_types()
 | 
|---|
| 551 | 
 | 
|---|
| 552 |                 state = thread['state'].cast(cfa_t.thread_state)
 | 
|---|
| 553 |                 try:
 | 
|---|
| 554 |                         if state == gdb.parse_and_eval('Halted'):
 | 
|---|
| 555 |                                 print('Cannot switch to a terminated thread')
 | 
|---|
| 556 |                                 return
 | 
|---|
| 557 | 
 | 
|---|
| 558 |                         if state == gdb.parse_and_eval('Start'):
 | 
|---|
| 559 |                                 print('Cannjot switch to a thread not yet run')
 | 
|---|
| 560 |                                 return
 | 
|---|
| 561 |                 except:
 | 
|---|
| 562 |                         print("here 2")
 | 
|---|
| 563 |                         return
 | 
|---|
| 564 | 
 | 
|---|
| 565 | 
 | 
|---|
| 566 |                 context = thread['context']
 | 
|---|
| 567 | 
 | 
|---|
| 568 | 
 | 
|---|
| 569 | 
 | 
|---|
| 570 |                 # must be at frame 0 to set pc register
 | 
|---|
| 571 |                 gdb.execute('select-frame 0')
 | 
|---|
| 572 |                 if gdb.selected_frame().architecture().name() != 'i386:x86-64':
 | 
|---|
| 573 |                         print('gdb debugging only supported for i386:x86-64 for now')
 | 
|---|
| 574 |                         return
 | 
|---|
| 575 | 
 | 
|---|
| 576 |                 # gdb seems to handle things much better if we pretend we just entered the context switch
 | 
|---|
| 577 |                 # pretend the pc is __cfactx_switch and adjust the sp, base pointer doesn't need to change
 | 
|---|
| 578 |                 # lookup for sp,fp and uSwitch
 | 
|---|
| 579 |                 xsp = context['SP'] + 40 # 40 = 5 64bit registers : %r15, %r14, %r13, %r12, %rbx WARNING: x64 specific
 | 
|---|
| 580 |                 xfp = context['FP']
 | 
|---|
| 581 | 
 | 
|---|
| 582 |                 # convert string so we can strip out the address
 | 
|---|
| 583 |                 try:
 | 
|---|
| 584 |                         xpc = get_addr(gdb.parse_and_eval('__cfactx_switch').address)
 | 
|---|
| 585 |                 except:
 | 
|---|
| 586 |                         print("here")
 | 
|---|
| 587 |                         return
 | 
|---|
| 588 | 
 | 
|---|
| 589 |                 # push sp, fp, pc into a global stack
 | 
|---|
| 590 |                 global STACK
 | 
|---|
| 591 |                 sp = gdb.parse_and_eval('$sp')
 | 
|---|
| 592 |                 fp = gdb.parse_and_eval('$fp')
 | 
|---|
| 593 |                 pc = gdb.parse_and_eval('$pc')
 | 
|---|
| 594 |                 stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
 | 
|---|
| 595 |                 STACK.append(stack_info)
 | 
|---|
| 596 | 
 | 
|---|
| 597 |                 # update registers for new task
 | 
|---|
| 598 |                 # print('switching to {} ({}) : [{}, {}, {}]'.format(thread['self_cor']['name'].string(), str(thread), str(xsp), str(xfp), str(xpc)))
 | 
|---|
| 599 |                 print('switching to thread {} ({})'.format(str(thread), thread['self_cor']['name'].string()))
 | 
|---|
| 600 |                 gdb.execute('set $rsp={}'.format(xsp))
 | 
|---|
| 601 |                 gdb.execute('set $rbp={}'.format(xfp))
 | 
|---|
| 602 |                 gdb.execute('set $pc={}'.format(xpc))
 | 
|---|
| 603 | 
 | 
|---|
| 604 |         def find_matching_gdb_thread_id():
 | 
|---|
| 605 |                 """
 | 
|---|
| 606 |                 Parse the str from info thread to get the number
 | 
|---|
| 607 |                 """
 | 
|---|
| 608 |                 info_thread_str = gdb.execute('info thread', to_string=True).splitlines()
 | 
|---|
| 609 |                 for thread_str in info_thread_str:
 | 
|---|
| 610 |                         if thread_str.find('this={}'.format(task)) != -1:
 | 
|---|
| 611 |                                 thread_id_pattern = r'^\*?\s+(\d+)\s+Thread'
 | 
|---|
| 612 |                                 # retrive gdb thread id
 | 
|---|
| 613 |                                 return re.match(thread_id_pattern, thread_str).group(1)
 | 
|---|
| 614 | 
 | 
|---|
| 615 |                         # check if the task is running or not
 | 
|---|
| 616 |                         if task_state == gdb.parse_and_eval('uBaseTask::Running'):
 | 
|---|
| 617 |                                 # find the equivalent thread from info thread
 | 
|---|
| 618 |                                 gdb_thread_id = find_matching_gdb_thread_id()
 | 
|---|
| 619 |                                 if gdb_thread_id is None:
 | 
|---|
| 620 |                                         print('cannot find the thread id to switch to')
 | 
|---|
| 621 |                                         return
 | 
|---|
| 622 |                                 # switch to that thread based using thread command
 | 
|---|
| 623 |                                 gdb.execute('thread {}'.format(gdb_thread_id))
 | 
|---|
| 624 | 
 | 
|---|
| 625 |         def switchto_id(self, tid, cluster):
 | 
|---|
| 626 |                 """
 | 
|---|
| 627 |                 @cluster: cluster object
 | 
|---|
| 628 |                 @tid: int
 | 
|---|
| 629 |                 """
 | 
|---|
| 630 |                 threads = lookup_threads_by_cluster( cluster )
 | 
|---|
| 631 | 
 | 
|---|
| 632 |                 for t in threads:
 | 
|---|
| 633 |                         if t.tid == tid:
 | 
|---|
| 634 |                                 self.switchto(t.value)
 | 
|---|
| 635 |                                 return
 | 
|---|
| 636 | 
 | 
|---|
| 637 |                 print("Cound not find thread by id '{}'".format(tid))
 | 
|---|
| 638 | 
 | 
|---|
| 639 |         def invoke(self, arg, from_tty):
 | 
|---|
| 640 |                 """
 | 
|---|
| 641 |                 @arg: str
 | 
|---|
| 642 |                 @from_tty: bool
 | 
|---|
| 643 |                 """
 | 
|---|
| 644 |                 if not is_cforall():
 | 
|---|
| 645 |                         return
 | 
|---|
| 646 | 
 | 
|---|
| 647 |                 argv = parse(arg)
 | 
|---|
| 648 |                 if argv[0].isdigit():
 | 
|---|
| 649 |                         cname = " ".join(argv[1:]) if len(argv) > 1 else None
 | 
|---|
| 650 |                         cluster = lookup_cluster(cname)
 | 
|---|
| 651 |                         if not cluster:
 | 
|---|
| 652 |                                 print("Could not find cluster '{}'".format(cname if cname else "Main Cluster"))
 | 
|---|
| 653 |                                 return
 | 
|---|
| 654 | 
 | 
|---|
| 655 |                         try:
 | 
|---|
| 656 |                                 tid = int(argv[0])
 | 
|---|
| 657 |                         except:
 | 
|---|
| 658 |                                 print("'{}' not a valid thread id".format(argv[0]))
 | 
|---|
| 659 |                                 print_usage(self)
 | 
|---|
| 660 |                                 return
 | 
|---|
| 661 | 
 | 
|---|
| 662 |                                 # by id, userCluster
 | 
|---|
| 663 |                         self.switchto_id(tid, cluster)
 | 
|---|
| 664 | 
 | 
|---|
| 665 |                 elif argv[0].startswith('0x') or argv[0].startswith('0X'):
 | 
|---|
| 666 |                         self.switchto(argv[0]) # by address, any cluster
 | 
|---|
| 667 | 
 | 
|---|
| 668 | ############
 | 
|---|
| 669 | class PrevThread(gdb.Command):
 | 
|---|
| 670 |         """Switch back to previous task on the stack"""
 | 
|---|
| 671 |         usage_msg = 'prevtask'
 | 
|---|
| 672 | 
 | 
|---|
| 673 |         def __init__(self):
 | 
|---|
| 674 |                 super(PrevThread, self).__init__('prevtask', gdb.COMMAND_USER)
 | 
|---|
| 675 | 
 | 
|---|
| 676 |         def invoke(self, arg, from_tty):
 | 
|---|
| 677 |                 """
 | 
|---|
| 678 |                 @arg: str
 | 
|---|
| 679 |                 @from_tty: bool
 | 
|---|
| 680 |                 """
 | 
|---|
| 681 |                 global STACK
 | 
|---|
| 682 |                 if len(STACK) != 0:
 | 
|---|
| 683 |                         # must be at frame 0 to set pc register
 | 
|---|
| 684 |                         gdb.execute('select-frame 0')
 | 
|---|
| 685 | 
 | 
|---|
| 686 |                         # pop stack
 | 
|---|
| 687 |                         stack_info = STACK.pop()
 | 
|---|
| 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('empty stack')
 | 
|---|
| 699 | 
 | 
|---|
| 700 | class ResetOriginFrame(gdb.Command):
 | 
|---|
| 701 |         """Reset to the origin frame prior to continue execution again"""
 | 
|---|
| 702 |         usage_msg = 'resetOriginFrame'
 | 
|---|
| 703 |         def __init__(self):
 | 
|---|
| 704 |                 super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
 | 
|---|
| 705 | 
 | 
|---|
| 706 |         def invoke(self, arg, from_tty):
 | 
|---|
| 707 |                 """
 | 
|---|
| 708 |                 @arg: str
 | 
|---|
| 709 |                 @from_tty: bool
 | 
|---|
| 710 |                 """
 | 
|---|
| 711 |                 global STACK
 | 
|---|
| 712 |                 if len(STACK) != 0:
 | 
|---|
| 713 |                         stack_info = STACK.pop(0)
 | 
|---|
| 714 |                         STACK.clear()
 | 
|---|
| 715 |                         pc = get_addr(stack_info.pc)
 | 
|---|
| 716 |                         sp = stack_info.sp
 | 
|---|
| 717 |                         fp = stack_info.fp
 | 
|---|
| 718 | 
 | 
|---|
| 719 |                         # pop sp, fp, pc from global stack
 | 
|---|
| 720 |                         adjust_stack(pc, fp, sp)
 | 
|---|
| 721 | 
 | 
|---|
| 722 |                         # must be at C++ frame to access C++ vars
 | 
|---|
| 723 |                         gdb.execute('frame 1')
 | 
|---|
| 724 |                 #else:
 | 
|---|
| 725 |                         #print('reset: empty stack') #probably does not have to print msg
 | 
|---|
| 726 | 
 | 
|---|
| 727 | Clusters()
 | 
|---|
| 728 | Processors()
 | 
|---|
| 729 | ResetOriginFrame()
 | 
|---|
| 730 | PrevThread()
 | 
|---|
| 731 | Threads()
 | 
|---|
| 732 | Thread()
 | 
|---|
| 733 | 
 | 
|---|
| 734 | # Local Variables: #
 | 
|---|
| 735 | # mode: Python #
 | 
|---|
| 736 | # End: #
 | 
|---|