1#!/usr/bin/python
2
3# Copyright (C) 2013 Oracle.
4#
5# Licensed under the Open Software License version 1.1
6
7import sqlite3
8import sys
9import re
10import subprocess
11
12try:
13    con = sqlite3.connect('smatch_db.sqlite')
14except sqlite3.Error, e:
15    print "Error %s:" % e.args[0]
16    sys.exit(1)
17
18def 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
34function_ptrs = []
35searched_ptrs = []
36def 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
48def 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
56db_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
114def 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
196def 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
208def 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
235def 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
259def 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
284def 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
306def 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
323def 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
330def 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
336def 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
352def 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
363def print_caller_info(filename, func, my_type = ""):
364    ptrs = get_function_pointers(func)
365    get_caller_info(filename, ptrs, my_type)
366
367def 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
385def 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
403def 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
412def 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
426def 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
444def 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
452def caller_info_values(filename, func):
453    ptrs = get_function_pointers(func)
454    print_caller_values(filename, func, ptrs)
455
456def 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
469def 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
481def 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
493def 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
500def print_fn_ptrs(func):
501    ptrs = get_function_pointers(func)
502    if not ptrs:
503        return
504    print "%s = " %(func),
505    print(ptrs)
506
507def 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
514def 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
524printed_funcs = []
525def 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
542def print_call_tree(func):
543    global printed_funcs
544    printed_funcs = []
545    call_tree_helper(func)
546
547def 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
553def 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
563def 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
573def 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
582def rl_is_treat_untagged(txt):
583    if "[u]" in txt:
584        return 1;
585
586    return 0
587
588def 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
614def 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
654def 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
673def 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
694def 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
700def 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
706def 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
712if len(sys.argv) < 2:
713    usage()
714
715if len(sys.argv) == 2:
716    func = sys.argv[1]
717    print_caller_info("", func)
718elif 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)
724elif 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)
731elif sys.argv[1] == "function_ptr" or sys.argv[1] == "fn_ptr":
732    func = sys.argv[2]
733    print_fn_ptrs(func)
734elif sys.argv[1] == "return_states":
735    func = sys.argv[2]
736    print_return_states(func)
737    print "================================================"
738    print_return_implies(func)
739elif sys.argv[1] == "return_implies":
740    func = sys.argv[2]
741    print_return_implies(func)
742elif 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)
746elif sys.argv[1] == "data_info":
747    struct_type = sys.argv[2]
748    member = sys.argv[3]
749    print_data_info(struct_type, member)
750elif sys.argv[1] == "call_tree":
751    func = sys.argv[2]
752    print_call_tree(func)
753elif sys.argv[1] == "find_tagged":
754    func = sys.argv[2]
755    param = int(sys.argv[3])
756    find_tagged(func, param, 0, [])
757elif sys.argv[1] == "parse_warns_tagged":
758    filename = sys.argv[2]
759    parse_warns_tagged(filename)
760elif 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)
768elif 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)
774elif sys.argv[1] == "functions":
775    member = sys.argv[2]
776    print_functions(member)
777elif 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)
783elif sys.argv[1] == "locals":
784    if len(sys.argv) != 3:
785        usage()
786    filename = sys.argv[2]
787    print_locals(filename);
788elif 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)
796else:
797    usage()
798