| [1cfdee9] | 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 thread_state')
 | 
|---|
 | 26 | 
 | 
|---|
| [96df7c9c] | 27 | class ThreadInfo:
 | 
|---|
| [9987d79] | 28 |         tid = 0
 | 
|---|
 | 29 |         cluster = None
 | 
|---|
 | 30 |         value = None
 | 
|---|
| [1cfdee9] | 31 | 
 | 
|---|
| [9987d79] | 32 |         def __init__(self, cluster, value):
 | 
|---|
 | 33 |                 self.cluster = cluster
 | 
|---|
 | 34 |                 self.value = value
 | 
|---|
| [1cfdee9] | 35 | 
 | 
|---|
| [9987d79] | 36 |         def is_system(self):
 | 
|---|
 | 37 |                 return False
 | 
|---|
| [1cfdee9] | 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():
 | 
|---|
| [9987d79] | 49 |         return True
 | 
|---|
| [1cfdee9] | 50 | 
 | 
|---|
 | 51 | def get_cfa_types():
 | 
|---|
| [9987d79] | 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(),
 | 
|---|
| [ff79d5e] | 57 |                                    thread_state = gdb.lookup_type('enum __Coroutine_State'))
 | 
|---|
| [1cfdee9] | 58 | 
 | 
|---|
 | 59 | def get_addr(addr):
 | 
|---|
| [9987d79] | 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()
 | 
|---|
| [1cfdee9] | 71 | 
 | 
|---|
| [bb75b4e] | 72 | def print_usage(obj):
 | 
|---|
| [9987d79] | 73 |         print(obj.__doc__)
 | 
|---|
| [1cfdee9] | 74 | 
 | 
|---|
 | 75 | def parse(args):
 | 
|---|
| [9987d79] | 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
 | 
|---|
| [1cfdee9] | 89 | 
 | 
|---|
 | 90 | def get_cluster_root():
 | 
|---|
| [9987d79] | 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
 | 
|---|
| [1cfdee9] | 98 | 
 | 
|---|
| [d8b17e2] | 99 | def 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
 | 
|---|
| [9987d79] | 107 | 
 | 
|---|
 | 108 | def 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 | 
 | 
|---|
| [d8b17e2] | 128 | def 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 | 
 | 
|---|
 | 149 | def 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 | 
 | 
|---|
 | 160 | def tls_for_proc(proc):
 | 
|---|
 | 161 |         return tls_for_pthread(proc['_X13kernel_threadm_1'])
 | 
|---|
 | 162 | 
 | 
|---|
 | 163 | def thread_for_pthread(pthrd):
 | 
|---|
 | 164 |         return tls_for_pthread(pthrd)['_X11this_threadVPS7$thread_1']
 | 
|---|
 | 165 | 
 | 
|---|
 | 166 | def thread_for_proc(proc):
 | 
|---|
 | 167 |         return tls_for_proc(proc)['_X11this_threadVPS7$thread_1']
 | 
|---|
 | 168 | 
 | 
|---|
 | 169 | 
 | 
|---|
 | 170 | 
 | 
|---|
 | 171 | def 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
 | 
|---|
| [96df7c9c] | 178 | 
 | 
|---|
| [1cfdee9] | 179 | def lookup_cluster(name = None):
 | 
|---|
| [9987d79] | 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
 | 
|---|
| [1cfdee9] | 211 | 
 | 
|---|
| [96df7c9c] | 212 | def lookup_threads_by_cluster(cluster):
 | 
|---|
| [9987d79] | 213 |                 # Iterate through a circular linked list of threads and accumulate them in an array
 | 
|---|
 | 214 |                 threads = []
 | 
|---|
| [96df7c9c] | 215 | 
 | 
|---|
| [9987d79] | 216 |                 cfa_t = get_cfa_types()
 | 
|---|
 | 217 |                 root = cluster['_X7threadsS8__dllist_S7$thread__1']['_X4headPY15__TYPE_generic__1'].cast(cfa_t.thread_ptr)
 | 
|---|
| [96df7c9c] | 218 | 
 | 
|---|
| [9987d79] | 219 |                 if root == 0x0 or root.address == 0x0:
 | 
|---|
 | 220 |                         print('There are no tasks for cluster: {}'.format(cluster))
 | 
|---|
 | 221 |                         return threads
 | 
|---|
| [96df7c9c] | 222 | 
 | 
|---|
| [9987d79] | 223 |                 curr = root
 | 
|---|
 | 224 |                 tid = 0
 | 
|---|
 | 225 |                 sid = -1
 | 
|---|
| [96df7c9c] | 226 | 
 | 
|---|
| [9987d79] | 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
 | 
|---|
| [96df7c9c] | 235 | 
 | 
|---|
| [9987d79] | 236 |                         threads.append(t)
 | 
|---|
| [96df7c9c] | 237 | 
 | 
|---|
| [9987d79] | 238 |                         curr = curr['node']['next']
 | 
|---|
 | 239 |                         if curr == root or curr == 0x0:
 | 
|---|
 | 240 |                                 break
 | 
|---|
| [96df7c9c] | 241 | 
 | 
|---|
| [9987d79] | 242 |                 return threads
 | 
|---|
| [96df7c9c] | 243 | 
 | 
|---|
| [1cfdee9] | 244 | def system_thread(thread):
 | 
|---|
| [9987d79] | 245 |         return False
 | 
|---|
| [1cfdee9] | 246 | 
 | 
|---|
 | 247 | def adjust_stack(pc, fp, sp):
 | 
|---|
| [9987d79] | 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))
 | 
|---|
| [1cfdee9] | 252 | 
 | 
|---|
 | 253 | ############################ COMMAND IMPLEMENTATION #########################
 | 
|---|
 | 254 | 
 | 
|---|
 | 255 | class Clusters(gdb.Command):
 | 
|---|
| [9987d79] | 256 |         """Cforall: Display currently known clusters
 | 
|---|
| [bb75b4e] | 257 | Usage:
 | 
|---|
| [9987d79] | 258 |         info clusters                 : print out all the clusters
 | 
|---|
| [bb75b4e] | 259 | """
 | 
|---|
| [1cfdee9] | 260 | 
 | 
|---|
| [9987d79] | 261 |         def __init__(self):
 | 
|---|
 | 262 |                 super(Clusters, self).__init__('info clusters', gdb.COMMAND_USER)
 | 
|---|
| [1cfdee9] | 263 | 
 | 
|---|
| [9987d79] | 264 |         def print_cluster(self, cluster_name, cluster_address):
 | 
|---|
 | 265 |                 print('{:>20}  {:>20}'.format(cluster_name, cluster_address))
 | 
|---|
| [1cfdee9] | 266 | 
 | 
|---|
| [9987d79] | 267 |         #entry point from gdb
 | 
|---|
 | 268 |         def invoke(self, arg, from_tty):
 | 
|---|
 | 269 |                 if not is_cforall():
 | 
|---|
 | 270 |                         return
 | 
|---|
| [1cfdee9] | 271 | 
 | 
|---|
| [9987d79] | 272 |                 if arg:
 | 
|---|
 | 273 |                         print("info clusters does not take arguments")
 | 
|---|
 | 274 |                         print_usage(self)
 | 
|---|
 | 275 |                         return
 | 
|---|
| [1cfdee9] | 276 | 
 | 
|---|
| [9987d79] | 277 |                 self.print_cluster('Name', 'Address')
 | 
|---|
| [1cfdee9] | 278 | 
 | 
|---|
| [9987d79] | 279 |                 for c in all_clusters():
 | 
|---|
 | 280 |                         self.print_cluster(c['_X4namePKc_1'].string(), str(c))
 | 
|---|
| [1cfdee9] | 281 | 
 | 
|---|
| [9987d79] | 282 |                 print("")
 | 
|---|
| [1cfdee9] | 283 | 
 | 
|---|
 | 284 | ############
 | 
|---|
 | 285 | class Processors(gdb.Command):
 | 
|---|
| [9987d79] | 286 |         """Cforall: Display currently known processors
 | 
|---|
| [bb75b4e] | 287 | Usage:
 | 
|---|
| [d8b17e2] | 288 |         info processors                 : print out all the processors
 | 
|---|
| [9987d79] | 289 |         info processors <cluster_name>  : print out all processors in a given cluster
 | 
|---|
| [bb75b4e] | 290 | """
 | 
|---|
| [1cfdee9] | 291 | 
 | 
|---|
| [9987d79] | 292 |         def __init__(self):
 | 
|---|
 | 293 |                 super(Processors, self).__init__('info processors', gdb.COMMAND_USER)
 | 
|---|
 | 294 | 
 | 
|---|
| [d8b17e2] | 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
 | 
|---|
| [9987d79] | 300 | 
 | 
|---|
| [d8b17e2] | 301 |                         status = 'Idle' if midle or end else 'Active'
 | 
|---|
 | 302 |                 else:
 | 
|---|
| [9987d79] | 303 |                         stop_count  = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
 | 
|---|
| [d8b17e2] | 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
 | 
|---|
| [9987d79] | 319 | 
 | 
|---|
| [d8b17e2] | 320 |                 print('{:>20}  {}'.format('Thread', tname))
 | 
|---|
 | 321 |                 print('{:>20}  {}'.format('TLS', tls))
 | 
|---|
| [9987d79] | 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 | 
 | 
|---|
| [d8b17e2] | 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)
 | 
|---|
| [9987d79] | 352 | 
 | 
|---|
 | 353 |                 print()
 | 
|---|
| [1cfdee9] | 354 | 
 | 
|---|
 | 355 | ############
 | 
|---|
 | 356 | class Threads(gdb.Command):
 | 
|---|
| [9987d79] | 357 |         """Cforall: Display currently known threads
 | 
|---|
| [96df7c9c] | 358 | Usage:
 | 
|---|
| [9987d79] | 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)
 | 
|---|
| [96df7c9c] | 427 | 
 | 
|---|
| [bb75b4e] | 428 | 
 | 
|---|
 | 429 | ############
 | 
|---|
 | 430 | class Thread(gdb.Command):
 | 
|---|
| [9987d79] | 431 |         """Cforall: Switch to specified user threads
 | 
|---|
 | 432 | Usage:
 | 
|---|
 | 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
 | 
|---|
| [1cfdee9] | 574 | 
 | 
|---|
 | 575 | ############
 | 
|---|
 | 576 | class PrevThread(gdb.Command):
 | 
|---|
| [9987d79] | 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')
 | 
|---|
| [1cfdee9] | 606 | 
 | 
|---|
 | 607 | class ResetOriginFrame(gdb.Command):
 | 
|---|
| [9987d79] | 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
 | 
|---|
| [1cfdee9] | 633 | 
 | 
|---|
 | 634 | Clusters()
 | 
|---|
 | 635 | Processors()
 | 
|---|
 | 636 | ResetOriginFrame()
 | 
|---|
 | 637 | PrevThread()
 | 
|---|
 | 638 | Threads()
 | 
|---|
| [9987d79] | 639 | Thread()
 | 
|---|
| [1cfdee9] | 640 | 
 | 
|---|
 | 641 | # Local Variables: #
 | 
|---|
 | 642 | # mode: Python #
 | 
|---|
 | 643 | # End: #
 | 
|---|