1 #!/usr/bin/python
   2 
   3 # Copyright (C) 2013 Oracle.
   4 #
   5 # Licensed under the Open Software License version 1.1
   6 
   7 import sqlite3
   8 import sys
   9 import re
  10 import subprocess
  11 
  12 try:
  13     con = sqlite3.connect('smatch_db.sqlite')
  14 except sqlite3.Error, e:
  15     print "Error %s:" % e.args[0]
  16     sys.exit(1)
  17 
  18 def usage():
  19     print "%s" %(sys.argv[0])
  20     print "<function> - how a function is called"
  21     print "info <type> - how a function is called, filtered by type"
  22     print "return_states <function> - what a function returns"
  23     print "call_tree <function> - show the call tree"
  24     print "where <struct_type> <member> - where a struct member is set"
  25     print "type_size <struct_type> <member> - how a struct member is allocated"
  26     print "data_info <struct_type> <member> - information about a given data type"
  27     print "function_ptr <function> - which function pointers point to this"
  28     print "trace_param <function> <param> - trace where a parameter came from"
  29     print "find_tagged <function> <param> - find the source of a tagged value (arm64)"
  30     print "parse_warns_tagged <smatch_warns.txt> - parse warns file for summary of tagged issues (arm64)"
  31     print "locals <file> - print the local values in a file."
  32     sys.exit(1)
  33 
  34 function_ptrs = []
  35 searched_ptrs = []
  36 def get_function_pointers_helper(func):
  37     cur = con.cursor()
  38     cur.execute("select distinct ptr from function_ptr where function = '%s';" %(func))
  39     for row in cur:
  40         ptr = row[0]
  41         if ptr in function_ptrs:
  42             continue
  43         function_ptrs.append(ptr)
  44         if not ptr in searched_ptrs:
  45             searched_ptrs.append(ptr)
  46             get_function_pointers_helper(ptr)
  47 
  48 def get_function_pointers(func):
  49     global function_ptrs
  50     global searched_ptrs
  51     function_ptrs = [func]
  52     searched_ptrs = [func]
  53     get_function_pointers_helper(func)
  54     return function_ptrs
  55 
  56 db_types = {   0: "INTERNAL",
  57              101: "PARAM_CLEARED",
  58              103: "PARAM_LIMIT",
  59              104: "PARAM_FILTER",
  60             1001: "PARAM_VALUE",
  61             1002: "BUF_SIZE",
  62             1004: "CAPPED_DATA",
  63             1005: "RETURN_VALUE",
  64             1006: "DEREFERENCE",
  65             1007: "RANGE_CAP",
  66             1008: "LOCK_HELD",
  67             1009: "LOCK_RELEASED",
  68             1010: "ABSOLUTE_LIMITS",
  69             1012: "PARAM_ADD",
  70             1013: "PARAM_FREED",
  71             1014: "DATA_SOURCE",
  72             1015: "FUZZY_MAX",
  73             1016: "STR_LEN",
  74             1017: "ARRAY_LEN",
  75             1018: "CAPABLE",
  76             1019: "NS_CAPABLE",
  77             1020: "CONTAINER",
  78             1022: "TYPE_LINK",
  79             1023: "UNTRACKED_PARAM",
  80             1024: "CULL_PATH",
  81             1025: "PARAM_SET",
  82             1026: "PARAM_USED",
  83             1027: "BYTE_UNITS",
  84             1028: "COMPARE_LIMIT",
  85             1029: "PARAM_COMPARE",
  86             1030: "EXPECTS_TYPE",
  87             1031: "CONSTRAINT",
  88             1032: "PASSES_TYPE",
  89             1033: "CONSTRAINT_REQUIRED",
  90             1034: "BIT_INFO",
  91             1035: "NOSPEC",
  92             1036: "NOSPEC_WB",
  93             1037: "STMT_CNT",
  94             1038: "TERMINATED",
  95             1039: "SLEEP",
  96             1040: "PREEMPT_CNT",
  97             1041: "SMALLISH",
  98             1042: "FRESH_MTAG",
  99 
 100             8017: "USER_DATA",
 101             9017: "USER_DATA_SET",
 102             8018: "NO_OVERFLOW",
 103             8019: "NO_OVERFLOW_SIMPLE",
 104             8020: "LOCKED",
 105             8021: "UNLOCKED",
 106             9022: "HALF_LOCKED",
 107             9023: "LOCK_RESTORED",
 108             9024: "KNOWN_LOCKED",
 109             9025: "KNOWN_UNLOCKED",
 110             8023: "ATOMIC_INC",
 111             8024: "ATOMIC_DEC",
 112 };
 113 
 114 def add_range(rl, min_val, max_val):
 115     check_next = 0
 116     done = 0
 117     ret = []
 118     idx = 0
 119 
 120     if len(rl) == 0:
 121         return [[min_val, max_val]]
 122 
 123     for idx in range(len(rl)):
 124         cur_min = rl[idx][0]
 125         cur_max = rl[idx][1]
 126 
 127         # we already merged the new range but we might need to change later
 128         # ranges if they over lap with more than one
 129         if check_next:
 130             # join with added range
 131             if max_val + 1 == cur_min:
 132                 ret[len(ret) - 1][1] = cur_max
 133                 done = 1
 134                 break
 135             # don't overlap
 136             if max_val < cur_min:
 137                 ret.append([cur_min, cur_max])
 138                 done = 1
 139                 break
 140             # partially overlap
 141             if max_val < cur_max:
 142                 ret[len(ret) - 1][1] = cur_max
 143                 done = 1
 144                 break
 145             # completely overlap
 146             continue
 147 
 148         # join 2 ranges into one
 149         if max_val + 1 == cur_min:
 150             ret.append([min_val, cur_max])
 151             done = 1
 152             break
 153         # range is entirely below
 154         if max_val < cur_min:
 155             ret.append([min_val, max_val])
 156             ret.append([cur_min, cur_max])
 157             done = 1
 158             break
 159         # range is partially below
 160         if min_val < cur_min:
 161             if max_val <= cur_max:
 162                 ret.append([min_val, cur_max])
 163                 done = 1
 164                 break
 165             else:
 166                 ret.append([min_val, max_val])
 167                 check_next = 1
 168                 continue
 169         # range already included
 170         if max_val <= cur_max:
 171             ret.append([cur_min, cur_max])
 172             done = 1
 173             break;
 174         # range partially above
 175         if min_val <= cur_max:
 176             ret.append([cur_min, max_val])
 177             check_next = 1
 178             continue
 179         # join 2 ranges on the other side
 180         if min_val - 1 == cur_max:
 181             ret.append([cur_min, max_val])
 182             check_next = 1
 183             continue
 184         # range is above
 185         ret.append([cur_min, cur_max])
 186 
 187     if idx + 1 < len(rl):          # we hit a break statement
 188         ret = ret + rl[idx + 1:]
 189     elif done:                     # we hit a break on the last iteration
 190         pass
 191     elif not check_next:           # it's past the end of the rl
 192         ret.append([min_val, max_val])
 193 
 194     return ret;
 195 
 196 def rl_union(rl1, rl2):
 197     ret = []
 198     for r in rl1:
 199         ret = add_range(ret, r[0], r[1])
 200     for r in rl2:
 201         ret = add_range(ret, r[0], r[1])
 202 
 203     if (rl1 or rl2) and not ret:
 204         print "bug: merging %s + %s gives empty" %(rl1, rl2)
 205 
 206     return ret
 207 
 208 def txt_to_val(txt):
 209     if txt == "s64min":
 210         return -(2**63)
 211     elif txt == "s32min":
 212         return -(2**31)
 213     elif txt == "s16min":
 214         return -(2**15)
 215     elif txt == "s64max":
 216         return 2**63 - 1
 217     elif txt == "s32max":
 218         return 2**31 - 1
 219     elif txt == "s16max":
 220         return 2**15 - 1
 221     elif txt == "u64max":
 222         return 2**64 - 1
 223     elif txt == "ptr_max":
 224         return 2**64 - 1
 225     elif txt == "u32max":
 226         return 2**32 - 1
 227     elif txt == "u16max":
 228         return 2**16 - 1
 229     else:
 230         try:
 231             return int(txt)
 232         except ValueError:
 233             return 0
 234 
 235 def val_to_txt(val):
 236     if val == -(2**63):
 237         return "s64min"
 238     elif val == -(2**31):
 239         return "s32min"
 240     elif val == -(2**15):
 241         return "s16min"
 242     elif val == 2**63 - 1:
 243         return "s64max"
 244     elif val == 2**31 - 1:
 245         return "s32max"
 246     elif val == 2**15 - 1:
 247         return "s16max"
 248     elif val == 2**64 - 1:
 249         return "u64max"
 250     elif val == 2**32 - 1:
 251         return "u32max"
 252     elif val == 2**16 - 1:
 253         return "u16max"
 254     elif val < 0:
 255         return "(%d)" %(val)
 256     else:
 257         return "%d" %(val)
 258 
 259 def get_next_str(txt):
 260     val = ""
 261     parsed = 0
 262 
 263     if txt[0] == '(':
 264         parsed += 1
 265         for char in txt[1:]:
 266             if char == ')':
 267                 break
 268             parsed += 1
 269         val = txt[1:parsed]
 270         parsed += 1
 271     elif txt[0] == 's' or txt[0] == 'u':
 272         parsed += 6
 273         val = txt[:parsed]
 274     else:
 275         if txt[0] == '-':
 276             parsed += 1
 277         for char in txt[parsed:]:
 278             if char == '-' or char == '[':
 279                 break
 280             parsed += 1
 281         val = txt[:parsed]
 282     return [parsed, val]
 283 
 284 def txt_to_rl(txt):
 285     if len(txt) == 0:
 286         return []
 287 
 288     ret = []
 289     pairs = txt.split(",")
 290     for pair in pairs:
 291         cnt, min_str = get_next_str(pair)
 292         if cnt == len(pair):
 293             max_str = min_str
 294         else:
 295             cnt, max_str = get_next_str(pair[cnt + 1:])
 296         min_val = txt_to_val(min_str)
 297         max_val = txt_to_val(max_str)
 298         ret.append([min_val, max_val])
 299 
 300 #    Hm...  Smatch won't call INT_MAX s32max if the variable is unsigned.
 301 #    if txt != rl_to_txt(ret):
 302 #        print "bug: converting: text = %s rl = %s internal = %s" %(txt, rl_to_txt(ret), ret)
 303 
 304     return ret
 305 
 306 def rl_to_txt(rl):
 307     ret = ""
 308     for idx in range(len(rl)):
 309         cur_min = rl[idx][0]
 310         cur_max = rl[idx][1]
 311 
 312         if idx != 0:
 313             ret += ","
 314 
 315         if cur_min == cur_max:
 316             ret += val_to_txt(cur_min)
 317         else:
 318             ret += val_to_txt(cur_min)
 319             ret += "-"
 320             ret += val_to_txt(cur_max)
 321     return ret
 322 
 323 def type_to_str(type_int):
 324 
 325     t = int(type_int)
 326     if db_types.has_key(t):
 327         return db_types[t]
 328     return type_int
 329 
 330 def type_to_int(type_string):
 331     for k in db_types.keys():
 332         if db_types[k] == type_string:
 333             return k
 334     return -1
 335 
 336 def display_caller_info(printed, cur, param_names):
 337     for txt in cur:
 338         if not printed:
 339             print "file | caller | function | type | parameter | key | value |"
 340         printed = 1
 341 
 342         parameter = int(txt[6])
 343         key = txt[7]
 344         if len(param_names) and parameter in param_names:
 345             key = key.replace("$", param_names[parameter])
 346 
 347         print "%20s | %20s | %20s |" %(txt[0], txt[1], txt[2]),
 348         print " %10s |" %(type_to_str(txt[5])),
 349         print " %d | %s | %s" %(parameter, key, txt[8])
 350     return printed
 351 
 352 def get_caller_info(filename, ptrs, my_type):
 353     cur = con.cursor()
 354     param_names = get_param_names(filename, func)
 355     printed = 0
 356     type_filter = ""
 357     if my_type != "":
 358         type_filter = "and type = %d" %(type_to_int(my_type))
 359     for ptr in ptrs:
 360         cur.execute("select * from caller_info where function = '%s' %s;" %(ptr, type_filter))
 361         printed = display_caller_info(printed, cur, param_names)
 362 
 363 def print_caller_info(filename, func, my_type = ""):
 364     ptrs = get_function_pointers(func)
 365     get_caller_info(filename, ptrs, my_type)
 366 
 367 def merge_values(param_names, vals, cur):
 368     for txt in cur:
 369         parameter = int(txt[0])
 370         name = txt[1]
 371         rl = txt_to_rl(txt[2])
 372         if parameter in param_names:
 373             name = name.replace("$", param_names[parameter])
 374 
 375         if not parameter in vals:
 376             vals[parameter] = {}
 377 
 378         # the first item on the list is the number of rows.  it's incremented
 379         # every time we call merge_values().
 380         if name in vals[parameter]:
 381             vals[parameter][name] = [vals[parameter][name][0] + 1, rl_union(vals[parameter][name][1], rl)]
 382         else:
 383             vals[parameter][name] = [1, rl]
 384 
 385 def get_param_names(filename, func):
 386     cur = con.cursor()
 387     param_names = {}
 388     cur.execute("select parameter, value from parameter_name where file = '%s' and function = '%s';" %(filename, func))
 389     for txt in cur:
 390         parameter = int(txt[0])
 391         name = txt[1]
 392         param_names[parameter] = name
 393     if len(param_names):
 394         return param_names
 395 
 396     cur.execute("select parameter, value from parameter_name where function = '%s';" %(func))
 397     for txt in cur:
 398         parameter = int(txt[0])
 399         name = txt[1]
 400         param_names[parameter] = name
 401     return param_names
 402 
 403 def get_caller_count(ptrs):
 404     cur = con.cursor()
 405     count = 0
 406     for ptr in ptrs:
 407         cur.execute("select count(distinct(call_id)) from caller_info where function = '%s';" %(ptr))
 408         for txt in cur:
 409             count += int(txt[0])
 410     return count
 411 
 412 def print_merged_caller_values(filename, func, ptrs, param_names, call_cnt):
 413     cur = con.cursor()
 414     vals = {}
 415     for ptr in ptrs:
 416         cur.execute("select parameter, key, value from caller_info where function = '%s' and type = %d;" %(ptr, type_to_int("PARAM_VALUE")))
 417         merge_values(param_names, vals, cur);
 418 
 419     for param in sorted(vals):
 420         for name in sorted(vals[param]):
 421             if vals[param][name][0] != call_cnt:
 422                 continue
 423             print "%d %s -> %s" %(param, name, rl_to_txt(vals[param][name][1]))
 424 
 425 
 426 def print_unmerged_caller_values(filename, func, ptrs, param_names):
 427     cur = con.cursor()
 428     for ptr in ptrs:
 429         prev = -1
 430         cur.execute("select file, caller, call_id, parameter, key, value from caller_info where function = '%s' and type = %d;" %(ptr, type_to_int("PARAM_VALUE")))
 431         for filename, caller, call_id, parameter, name, value in cur:
 432             if prev != int(call_id):
 433                 prev = int(call_id)
 434 
 435             parameter = int(parameter)
 436             if parameter < len(param_names):
 437                 name = name.replace("$", param_names[parameter])
 438             else:
 439                 name = name.replace("$", "$%d" %(parameter))
 440 
 441             print "%s | %s | %s | %s" %(filename, caller, name, value)
 442         print "=========================="
 443 
 444 def print_caller_values(filename, func, ptrs):
 445     param_names = get_param_names(filename, func)
 446     call_cnt = get_caller_count(ptrs)
 447 
 448     print_merged_caller_values(filename, func, ptrs, param_names, call_cnt)
 449     print "=========================="
 450     print_unmerged_caller_values(filename, func, ptrs, param_names)
 451 
 452 def caller_info_values(filename, func):
 453     ptrs = get_function_pointers(func)
 454     print_caller_values(filename, func, ptrs)
 455 
 456 def print_return_states(func):
 457     cur = con.cursor()
 458     cur.execute("select * from return_states where function = '%s';" %(func))
 459     count = 0
 460     for txt in cur:
 461         printed = 1
 462         if count == 0:
 463             print "file | function | return_id | return_value | type | param | key | value |"
 464         count += 1
 465         print "%s | %s | %2s | %13s" %(txt[0], txt[1], txt[3], txt[4]),
 466         print "| %13s |" %(type_to_str(txt[6])),
 467         print " %2d | %20s | %20s |" %(txt[7], txt[8], txt[9])
 468 
 469 def print_return_implies(func):
 470     cur = con.cursor()
 471     cur.execute("select * from return_implies where function = '%s';" %(func))
 472     count = 0
 473     for txt in cur:
 474         if not count:
 475             print "file | function | type | param | key | value |"
 476         count += 1
 477         print "%15s | %15s" %(txt[0], txt[1]),
 478         print "| %15s" %(type_to_str(txt[4])),
 479         print "| %3d | %s | %15s |" %(txt[5], txt[6], txt[7])
 480 
 481 def print_type_size(struct_type, member):
 482     cur = con.cursor()
 483     cur.execute("select * from type_size where type like '(struct %s)->%s';" %(struct_type, member))
 484     print "type | size"
 485     for txt in cur:
 486         print "%-15s | %s" %(txt[0], txt[1])
 487 
 488     cur.execute("select * from function_type_size where type like '(struct %s)->%s';" %(struct_type, member))
 489     print "file | function | type | size"
 490     for txt in cur:
 491         print "%-15s | %-15s | %-15s | %s" %(txt[0], txt[1], txt[2], txt[3])
 492 
 493 def print_data_info(struct_type, member):
 494     cur = con.cursor()
 495     cur.execute("select * from data_info where data like '(struct %s)->%s';" %(struct_type, member))
 496     print "file | data | type | value"
 497     for txt in cur:
 498         print "%-15s | %-15s | %-15s | %s" %(txt[0], txt[1], type_to_str(txt[2]), txt[3])
 499 
 500 def print_fn_ptrs(func):
 501     ptrs = get_function_pointers(func)
 502     if not ptrs:
 503         return
 504     print "%s = " %(func),
 505     print(ptrs)
 506 
 507 def print_functions(member):
 508     cur = con.cursor()
 509     cur.execute("select * from function_ptr where ptr like '%%->%s';" %(member))
 510     print "File | Pointer | Function | Static"
 511     for txt in cur:
 512         print "%-15s | %-15s | %-15s | %s" %(txt[0], txt[2], txt[1], txt[3])
 513 
 514 def get_callers(func):
 515     ret = []
 516     cur = con.cursor()
 517     ptrs = get_function_pointers(func)
 518     for ptr in ptrs:
 519         cur.execute("select distinct caller from caller_info where function = '%s';" %(ptr))
 520         for row in cur:
 521             ret.append(row[0])
 522     return ret
 523 
 524 printed_funcs = []
 525 def call_tree_helper(func, indent = 0):
 526     global printed_funcs
 527     if func in printed_funcs:
 528         return
 529     print "%s%s()" %(" " * indent, func)
 530     if func == "too common":
 531         return
 532     if indent > 6:
 533         return
 534     printed_funcs.append(func)
 535     callers = get_callers(func)
 536     if len(callers) >= 20:
 537         print "Over 20 callers for %s()" %(func)
 538         return
 539     for caller in callers:
 540         call_tree_helper(caller, indent + 2)
 541 
 542 def print_call_tree(func):
 543     global printed_funcs
 544     printed_funcs = []
 545     call_tree_helper(func)
 546 
 547 def function_type_value(struct_type, member):
 548     cur = con.cursor()
 549     cur.execute("select * from function_type_value where type like '(struct %s)->%s';" %(struct_type, member))
 550     for txt in cur:
 551         print "%-30s | %-30s | %s | %s" %(txt[0], txt[1], txt[2], txt[3])
 552 
 553 def rl_too_big(txt):
 554     rl = txt_to_rl(txt)
 555     ret = ""
 556     for idx in range(len(rl)):
 557         cur_max = rl[idx][1]
 558         if (cur_max > 0xFFFFFFFFFFFFFF):
 559             return 1
 560 
 561     return 0
 562 
 563 def rl_has_min_untagged(txt):
 564     rl = txt_to_rl(txt)
 565     ret = ""
 566     for idx in range(len(rl)):
 567         cur_min = rl[idx][0]
 568         if (cur_min == 0xff80000000000000):
 569             return 1
 570 
 571     return 0
 572 
 573 def rl_is_tagged(txt):
 574     if not rl_too_big(txt):
 575         return 0
 576 
 577     if rl_has_min_untagged(txt):
 578         return 0
 579 
 580     return 1
 581 
 582 def rl_is_treat_untagged(txt):
 583     if "[u]" in txt:
 584         return 1;
 585 
 586     return 0
 587 
 588 def parse_warns_tagged(filename):
 589     proc = subprocess.Popen(['cat %s | grep "potentially tagged" | sort | uniq' %(filename)], shell=True, stdout=subprocess.PIPE)
 590     while True:
 591         line = proc.stdout.readline()
 592         if not line:
 593             break
 594 
 595         linepos = re.search("([^\s]+)", line).group(1)
 596         groupre = re.search("potentially tagged address \(([^,]+), ([^,]+), ([^\)]+)\)", line)
 597         groupre.group(1)
 598 
 599         func = groupre.group(1)
 600         param = int(groupre.group(2))
 601         var = groupre.group(3)
 602 
 603         if ("end" in var or "size" in var or "len" in var):
 604                 continue
 605 
 606         print "\n%s (func: %s, param: %d:%s) may be caused by:" %(linepos, func, param, var)
 607 
 608         if (param != -1):
 609                 if not find_tagged(func, param, 0, []):
 610                         print "    %s (param %d) (can't walk call tree)" % (func, param)
 611         else:
 612                 print "    %s (variable %s (can't walk call tree)" % (func, var)
 613 
 614 def find_tagged(func, param, caller_call_id, printed):
 615 
 616     callers = {}
 617     cur = con.cursor()
 618     ptrs = get_function_pointers(func)
 619     found = 0
 620 
 621     for ptr in ptrs:
 622         cur.execute("select call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr, param, type_to_int("DATA_SOURCE")))
 623 
 624         for row in cur:
 625             if (row[1][0] == '$'):
 626                 if row[0] not in callers:
 627                     callers[row[0]] = {}
 628                 callers[row[0]]["param"] = int(row[1][1])
 629 
 630     for ptr in ptrs:
 631         cur.execute("select caller, call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr, param, type_to_int("USER_DATA")))
 632 
 633         for row in cur:
 634             if not rl_is_tagged(row[2]):
 635                 continue
 636             if rl_is_treat_untagged(row[2]):
 637                 continue
 638             found = 1
 639             if row[1] not in callers:
 640                 callers[row[1]] = {}
 641             if "param" not in callers[row[1]]:
 642                 line = "    %s (param ?) -> %s (param %d)" % (row[0], func, param)
 643                 if line not in printed:
 644                         printed.append(line)
 645                         print line
 646                 continue
 647             if row[0] not in printed:
 648                 printed.append(row[0])
 649                 if not find_tagged(row[0], callers[row[1]]["param"], row[1], printed):
 650                     print "    %s (param %d)" % (row[0], param)
 651 
 652     return found
 653 
 654 def trace_callers(func, param):
 655     sources = []
 656     prev_type = 0
 657 
 658     cur = con.cursor()
 659     ptrs = get_function_pointers(func)
 660     for ptr in ptrs:
 661         cur.execute("select type, caller, value from caller_info where function = '%s' and (type = 0 or type = 1014 or type = 1028) and (parameter = -1 or parameter = %d);" %(ptr, param))
 662         for row in cur:
 663             data_type = int(row[0])
 664             if data_type == 1014:
 665                 sources.append((row[1], row[2]))
 666             elif data_type == 1028:
 667                 sources.append(("%", row[2])) # hack...
 668             elif data_type == 0 and prev_type == 0:
 669                 sources.append((row[1], ""))
 670             prev_type = data_type
 671     return sources
 672 
 673 def trace_param_helper(func, param, indent = 0):
 674     global printed_funcs
 675     if func in printed_funcs:
 676         return
 677     print "%s%s(param %d)" %(" " * indent, func, param)
 678     if func == "too common":
 679         return
 680     if indent > 20:
 681         return
 682     printed_funcs.append(func)
 683     sources = trace_callers(func, param)
 684     for path in sources:
 685 
 686         if len(path[1]) and path[1][0] == '$':
 687             p = int(re.findall('\d+', path[1][1:])[0])
 688             trace_param_helper(path[0], p, indent + 2)
 689         elif len(path[0]) and path[0][0] == '%':
 690             print "  %s%s" %(" " * indent, path[1])
 691         else:
 692             print "* %s%s %s" %(" " * (indent - 1), path[0], path[1])
 693 
 694 def trace_param(func, param):
 695     global printed_funcs
 696     printed_funcs = []
 697     print "tracing %s %d" %(func, param)
 698     trace_param_helper(func, param)
 699 
 700 def print_locals(filename):
 701     cur = con.cursor()
 702     cur.execute("select file,data,value from data_info where file = '%s' and type = 8029 and value != 0;" %(filename))
 703     for txt in cur:
 704         print "%s | %s | %s" %(txt[0], txt[1], txt[2])
 705 
 706 def constraint(struct_type, member):
 707     cur = con.cursor()
 708     cur.execute("select * from constraints_required where data like '(struct %s)->%s' or bound like '(struct %s)->%s';" %(struct_type, member, struct_type, member))
 709     for txt in cur:
 710         print "%-30s | %-30s | %s | %s" %(txt[0], txt[1], txt[2], txt[3])
 711 
 712 if len(sys.argv) < 2:
 713     usage()
 714 
 715 if len(sys.argv) == 2:
 716     func = sys.argv[1]
 717     print_caller_info("", func)
 718 elif sys.argv[1] == "info":
 719     my_type = ""
 720     if len(sys.argv) == 4:
 721         my_type = sys.argv[3]
 722     func = sys.argv[2]
 723     print_caller_info("", func, my_type)
 724 elif sys.argv[1] == "call_info":
 725     if len(sys.argv) != 4:
 726         usage()
 727     filename = sys.argv[2]
 728     func = sys.argv[3]
 729     caller_info_values(filename, func)
 730     print_caller_info(filename, func)
 731 elif sys.argv[1] == "function_ptr" or sys.argv[1] == "fn_ptr":
 732     func = sys.argv[2]
 733     print_fn_ptrs(func)
 734 elif sys.argv[1] == "return_states":
 735     func = sys.argv[2]
 736     print_return_states(func)
 737     print "================================================"
 738     print_return_implies(func)
 739 elif sys.argv[1] == "return_implies":
 740     func = sys.argv[2]
 741     print_return_implies(func)
 742 elif sys.argv[1] == "type_size" or sys.argv[1] == "buf_size":
 743     struct_type = sys.argv[2]
 744     member = sys.argv[3]
 745     print_type_size(struct_type, member)
 746 elif sys.argv[1] == "data_info":
 747     struct_type = sys.argv[2]
 748     member = sys.argv[3]
 749     print_data_info(struct_type, member)
 750 elif sys.argv[1] == "call_tree":
 751     func = sys.argv[2]
 752     print_call_tree(func)
 753 elif sys.argv[1] == "find_tagged":
 754     func = sys.argv[2]
 755     param = int(sys.argv[3])
 756     find_tagged(func, param, 0, [])
 757 elif sys.argv[1] == "parse_warns_tagged":
 758     filename = sys.argv[2]
 759     parse_warns_tagged(filename)
 760 elif sys.argv[1] == "where":
 761     if len(sys.argv) == 3:
 762         struct_type = "%"
 763         member = sys.argv[2]
 764     elif len(sys.argv) == 4:
 765         struct_type = sys.argv[2]
 766         member = sys.argv[3]
 767     function_type_value(struct_type, member)
 768 elif sys.argv[1] == "local":
 769     filename = sys.argv[2]
 770     variable = ""
 771     if len(sys.argv) == 4:
 772         variable = sys.argv[3]
 773     local_values(filename, variable)
 774 elif sys.argv[1] == "functions":
 775     member = sys.argv[2]
 776     print_functions(member)
 777 elif sys.argv[1] == "trace_param":
 778     if len(sys.argv) != 4:
 779         usage()
 780     func = sys.argv[2]
 781     param = int(sys.argv[3])
 782     trace_param(func, param)
 783 elif sys.argv[1] == "locals":
 784     if len(sys.argv) != 3:
 785         usage()
 786     filename = sys.argv[2]
 787     print_locals(filename);
 788 elif sys.argv[1] == "constraint":
 789     if len(sys.argv) == 3:
 790         struct_type = "%"
 791         member = sys.argv[2]
 792     elif len(sys.argv) == 4:
 793         struct_type = sys.argv[2]
 794         member = sys.argv[3]
 795     constraint(struct_type, member)
 796 else:
 797     usage()