3 # Copyright (C) 2013 Oracle.
5 # Licensed under the Open Software License version 1.1
13 con
= sqlite3
.connect('smatch_db.sqlite')
14 except sqlite3
.Error
, e
:
15 print "Error %s:" % e
.args
[0]
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."
36 def get_function_pointers_helper(func
):
38 cur
.execute("select distinct ptr from function_ptr where function = '%s';" %(func))
41 if ptr
in function_ptrs
:
43 function_ptrs
.append(ptr
)
44 if not ptr
in searched_ptrs
:
45 searched_ptrs
.append(ptr
)
46 get_function_pointers_helper(ptr
)
48 def get_function_pointers(func
):
51 function_ptrs
= [func
]
52 searched_ptrs
= [func
]
53 get_function_pointers_helper(func
)
56 db_types
= { 0: "INTERNAL",
67 1009: "LOCK_RELEASED",
68 1010: "ABSOLUTE_LIMITS",
79 1023: "UNTRACKED_PARAM",
84 1028: "COMPARE_LIMIT",
85 1029: "PARAM_COMPARE",
89 1033: "CONSTRAINT_REQUIRED",
101 9017: "USER_DATA_SET",
103 9019: "USER_PTR_SET",
105 8019: "NO_OVERFLOW_SIMPLE",
109 9023: "LOCK_RESTORED",
110 9024: "KNOWN_LOCKED",
111 9025: "KNOWN_UNLOCKED",
116 def add_range(rl
, min_val
, max_val
):
123 return [[min_val
, max_val
]]
125 for idx
in range(len(rl
)):
129 # we already merged the new range but we might need to change later
130 # ranges if they over lap with more than one
132 # join with added range
133 if max_val
+ 1 == cur_min
:
134 ret
[len(ret
) - 1][1] = cur_max
138 if max_val
< cur_min
:
139 ret
.append([cur_min
, cur_max
])
143 if max_val
< cur_max
:
144 ret
[len(ret
) - 1][1] = cur_max
150 # join 2 ranges into one
151 if max_val
+ 1 == cur_min
:
152 ret
.append([min_val
, cur_max
])
155 # range is entirely below
156 if max_val
< cur_min
:
157 ret
.append([min_val
, max_val
])
158 ret
.append([cur_min
, cur_max
])
161 # range is partially below
162 if min_val
< cur_min
:
163 if max_val
<= cur_max
:
164 ret
.append([min_val
, cur_max
])
168 ret
.append([min_val
, max_val
])
171 # range already included
172 if max_val
<= cur_max
:
173 ret
.append([cur_min
, cur_max
])
176 # range partially above
177 if min_val
<= cur_max
:
178 ret
.append([cur_min
, max_val
])
181 # join 2 ranges on the other side
182 if min_val
- 1 == cur_max
:
183 ret
.append([cur_min
, max_val
])
187 ret
.append([cur_min
, cur_max
])
189 if idx
+ 1 < len(rl
): # we hit a break statement
190 ret
= ret
+ rl
[idx
+ 1:]
191 elif done
: # we hit a break on the last iteration
193 elif not check_next
: # it's past the end of the rl
194 ret
.append([min_val
, max_val
])
198 def rl_union(rl1
, rl2
):
201 ret
= add_range(ret
, r
[0], r
[1])
203 ret
= add_range(ret
, r
[0], r
[1])
205 if (rl1
or rl2
) and not ret
:
206 print "bug: merging %s + %s gives empty" %(rl1
, rl2
)
213 elif txt
== "s32min":
215 elif txt
== "s16min":
217 elif txt
== "s64max":
219 elif txt
== "s32max":
221 elif txt
== "s16max":
223 elif txt
== "u64max":
225 elif txt
== "ptr_max":
227 elif txt
== "u32max":
229 elif txt
== "u16max":
240 elif val
== -(2**31):
242 elif val
== -(2**15):
244 elif val
== 2**63 - 1:
246 elif val
== 2**31 - 1:
248 elif val
== 2**15 - 1:
250 elif val
== 2**64 - 1:
252 elif val
== 2**32 - 1:
254 elif val
== 2**16 - 1:
261 def get_next_str(txt
):
273 elif txt
[0] == 's' or txt
[0] == 'u':
279 for char
in txt
[parsed
:]:
280 if char
== '-' or char
== '[':
291 pairs
= txt
.split(",")
293 cnt
, min_str
= get_next_str(pair
)
297 cnt
, max_str
= get_next_str(pair
[cnt
+ 1:])
298 min_val
= txt_to_val(min_str
)
299 max_val
= txt_to_val(max_str
)
300 ret
.append([min_val
, max_val
])
302 # Hm... Smatch won't call INT_MAX s32max if the variable is unsigned.
303 # if txt != rl_to_txt(ret):
304 # print "bug: converting: text = %s rl = %s internal = %s" %(txt, rl_to_txt(ret), ret)
310 for idx
in range(len(rl
)):
317 if cur_min
== cur_max
:
318 ret
+= val_to_txt(cur_min
)
320 ret
+= val_to_txt(cur_min
)
322 ret
+= val_to_txt(cur_max
)
325 def type_to_str(type_int
):
328 if db_types
.has_key(t
):
332 def type_to_int(type_string
):
333 for k
in db_types
.keys():
334 if db_types
[k
] == type_string
:
338 def display_caller_info(printed
, cur
, param_names
):
341 print "file | caller | function | type | parameter | key | value |"
344 parameter
= int(txt
[6])
346 if len(param_names
) and parameter
in param_names
:
347 key
= key
.replace("$", param_names
[parameter
])
349 print "%20s | %20s | %20s |" %(txt
[0], txt
[1], txt
[2]),
350 print " %10s |" %(type_to_str(txt
[5])),
351 print " %d | %s | %s" %(parameter
, key
, txt
[8])
354 def get_caller_info(filename
, ptrs
, my_type
):
356 param_names
= get_param_names(filename
, func
)
360 type_filter
= "and type = %d" %(type_to_int(my_type
))
362 cur
.execute("select * from caller_info where function = '%s' %s;" %(ptr
, type_filter
))
363 printed
= display_caller_info(printed
, cur
, param_names
)
365 def print_caller_info(filename
, func
, my_type
= ""):
366 ptrs
= get_function_pointers(func
)
367 get_caller_info(filename
, ptrs
, my_type
)
369 def merge_values(param_names
, vals
, cur
):
371 parameter
= int(txt
[0])
373 rl
= txt_to_rl(txt
[2])
374 if parameter
in param_names
:
375 name
= name
.replace("$", param_names
[parameter
])
377 if not parameter
in vals
:
380 # the first item on the list is the number of rows. it's incremented
381 # every time we call merge_values().
382 if name
in vals
[parameter
]:
383 vals
[parameter
][name
] = [vals
[parameter
][name
][0] + 1, rl_union(vals
[parameter
][name
][1], rl
)]
385 vals
[parameter
][name
] = [1, rl
]
387 def get_param_names(filename
, func
):
390 cur
.execute("select parameter, value from parameter_name where file = '%s' and function = '%s';" %(filename
, func
))
392 parameter
= int(txt
[0])
394 param_names
[parameter
] = name
398 cur
.execute("select parameter, value from parameter_name where function = '%s';" %(func))
400 parameter
= int(txt
[0])
402 param_names
[parameter
] = name
405 def get_caller_count(ptrs
):
409 cur
.execute("select count(distinct(call_id)) from caller_info where function = '%s';" %(ptr))
414 def print_merged_caller_values(filename
, func
, ptrs
, param_names
, call_cnt
):
418 cur
.execute("select parameter, key, value from caller_info where function = '%s' and type = %d;" %(ptr
, type_to_int("PARAM_VALUE")))
419 merge_values(param_names
, vals
, cur
);
421 for param
in sorted(vals
):
422 for name
in sorted(vals
[param
]):
423 if vals
[param
][name
][0] != call_cnt
:
425 print "%d %s -> %s" %(param
, name
, rl_to_txt(vals
[param
][name
][1]))
428 def print_unmerged_caller_values(filename
, func
, ptrs
, param_names
):
432 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")))
433 for filename
, caller
, call_id
, parameter
, name
, value
in cur
:
434 if prev
!= int(call_id
):
437 parameter
= int(parameter
)
438 if parameter
< len(param_names
):
439 name
= name
.replace("$", param_names
[parameter
])
441 name
= name
.replace("$", "$%d" %(parameter))
443 print "%s | %s | %s | %s" %(filename
, caller
, name
, value
)
444 print "=========================="
446 def print_caller_values(filename
, func
, ptrs
):
447 param_names
= get_param_names(filename
, func
)
448 call_cnt
= get_caller_count(ptrs
)
450 print_merged_caller_values(filename
, func
, ptrs
, param_names
, call_cnt
)
451 print "=========================="
452 print_unmerged_caller_values(filename
, func
, ptrs
, param_names
)
454 def caller_info_values(filename
, func
):
455 ptrs
= get_function_pointers(func
)
456 print_caller_values(filename
, func
, ptrs
)
458 def print_return_states(func
):
460 cur
.execute("select * from return_states where function = '%s';" %(func))
465 print "file | function | return_id | return_value | type | param | key | value |"
467 print "%s | %s | %2s | %13s" %(txt
[0], txt
[1], txt
[3], txt
[4]),
468 print "| %13s |" %(type_to_str(txt
[6])),
469 print " %2d | %20s | %20s |" %(txt
[7], txt
[8], txt
[9])
471 def print_return_implies(func
):
473 cur
.execute("select * from return_implies where function = '%s';" %(func))
477 print "file | function | type | param | key | value |"
479 print "%15s | %15s" %(txt
[0], txt
[1]),
480 print "| %15s" %(type_to_str(txt
[4])),
481 print "| %3d | %s | %15s |" %(txt
[5], txt
[6], txt
[7])
483 def print_type_size(struct_type
, member
):
485 cur
.execute("select * from type_size where type like '(struct %s)->%s';" %(struct_type
, member
))
488 print "%-15s | %s" %(txt
[0], txt
[1])
490 cur
.execute("select * from function_type_size where type like '(struct %s)->%s';" %(struct_type
, member
))
491 print "file | function | type | size"
493 print "%-15s | %-15s | %-15s | %s" %(txt
[0], txt
[1], txt
[2], txt
[3])
495 def print_data_info(struct_type
, member
):
497 cur
.execute("select * from data_info where data like '(struct %s)->%s';" %(struct_type
, member
))
498 print "file | data | type | value"
500 print "%-15s | %-15s | %-15s | %s" %(txt
[0], txt
[1], type_to_str(txt
[2]), txt
[3])
502 def print_fn_ptrs(func
):
503 ptrs
= get_function_pointers(func
)
506 print "%s = " %(func),
509 def print_functions(member
):
511 cur
.execute("select * from function_ptr where ptr like '%%->%s';" %(member))
512 print "File | Pointer | Function | Static"
514 print "%-15s | %-15s | %-15s | %s" %(txt
[0], txt
[2], txt
[1], txt
[3])
516 def get_callers(func
):
519 ptrs
= get_function_pointers(func
)
521 cur
.execute("select distinct caller from caller_info where function = '%s';" %(ptr))
527 def call_tree_helper(func
, indent
= 0):
529 if func
in printed_funcs
:
531 print "%s%s()" %(" " * indent
, func
)
532 if func
== "too common":
536 printed_funcs
.append(func
)
537 callers
= get_callers(func
)
538 if len(callers
) >= 20:
539 print "Over 20 callers for %s()" %(func)
541 for caller
in callers
:
542 call_tree_helper(caller
, indent
+ 2)
544 def print_call_tree(func
):
547 call_tree_helper(func
)
549 def function_type_value(struct_type
, member
):
551 cur
.execute("select * from function_type_value where type like '(struct %s)->%s';" %(struct_type
, member
))
553 print "%-30s | %-30s | %s | %s" %(txt
[0], txt
[1], txt
[2], txt
[3])
558 for idx
in range(len(rl
)):
560 if (cur_max
> 0xFFFFFFFFFFFFFF):
565 def rl_has_min_untagged(txt
):
568 for idx
in range(len(rl
)):
570 if (cur_min
== 0xff80000000000000):
575 def rl_is_tagged(txt
):
576 if not rl_too_big(txt
):
579 if rl_has_min_untagged(txt
):
584 def rl_is_treat_untagged(txt
):
590 def parse_warns_tagged(filename
):
591 proc
= subprocess
.Popen(['cat %s | grep "potentially tagged" | sort | uniq' %(filename)], shell
=True, stdout
=subprocess
.PIPE
)
593 line
= proc
.stdout
.readline()
597 linepos
= re
.search("([^\s]+)", line
).group(1)
598 groupre
= re
.search("potentially tagged address \(([^,]+), ([^,]+), ([^\)]+)\)", line
)
601 func
= groupre
.group(1)
602 param
= int(groupre
.group(2))
603 var
= groupre
.group(3)
605 if ("end" in var
or "size" in var
or "len" in var
):
608 print "\n%s (func: %s, param: %d:%s) may be caused by:" %(linepos
, func
, param
, var
)
611 if not find_tagged(func
, param
, 0, []):
612 print " %s (param %d) (can't walk call tree)" % (func
, param
)
614 print " %s (variable %s (can't walk call tree)" % (func
, var
)
616 def find_tagged(func
, param
, caller_call_id
, printed
):
620 ptrs
= get_function_pointers(func
)
624 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")))
627 if (row
[1][0] == '$'):
628 if row
[0] not in callers
:
630 callers
[row
[0]]["param"] = int(row
[1][1])
633 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")))
636 if not rl_is_tagged(row
[2]):
638 if rl_is_treat_untagged(row
[2]):
641 if row
[1] not in callers
:
643 if "param" not in callers
[row
[1]]:
644 line
= " %s (param ?) -> %s (param %d)" % (row
[0], func
, param
)
645 if line
not in printed
:
649 if row
[0] not in printed
:
650 printed
.append(row
[0])
651 if not find_tagged(row
[0], callers
[row
[1]]["param"], row
[1], printed
):
652 print " %s (param %d)" % (row
[0], param
)
656 def trace_callers(func
, param
):
661 ptrs
= get_function_pointers(func
)
663 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
))
665 data_type
= int(row
[0])
666 if data_type
== 1014:
667 sources
.append((row
[1], row
[2]))
668 elif data_type
== 1028:
669 sources
.append(("%", row
[2])) # hack...
670 elif data_type
== 0 and prev_type
== 0:
671 sources
.append((row
[1], ""))
672 prev_type
= data_type
675 def trace_param_helper(func
, param
, indent
= 0):
677 if func
in printed_funcs
:
679 print "%s%s(param %d)" %(" " * indent
, func
, param
)
680 if func
== "too common":
684 printed_funcs
.append(func
)
685 sources
= trace_callers(func
, param
)
688 if len(path
[1]) and path
[1][0] == '$':
689 p
= int(re
.findall('\d+', path
[1][1:])[0])
690 trace_param_helper(path
[0], p
, indent
+ 2)
691 elif len(path
[0]) and path
[0][0] == '%':
692 print " %s%s" %(" " * indent
, path
[1])
694 print "* %s%s %s" %(" " * (indent
- 1), path
[0], path
[1])
696 def trace_param(func
, param
):
699 print "tracing %s %d" %(func
, param
)
700 trace_param_helper(func
, param
)
702 def print_locals(filename
):
704 cur
.execute("select file,data,value from data_info where file = '%s' and type = 8029 and value != 0;" %(filename))
706 print "%s | %s | %s" %(txt
[0], txt
[1], txt
[2])
708 def constraint(struct_type
, member
):
710 cur
.execute("select * from constraints_required where data like '(struct %s)->%s' or bound like '(struct %s)->%s';" %(struct_type
, member
, struct_type
, member
))
712 print "%-30s | %-30s | %s | %s" %(txt
[0], txt
[1], txt
[2], txt
[3])
714 if len(sys
.argv
) < 2:
717 if len(sys
.argv
) == 2:
719 print_caller_info("", func
)
720 elif sys
.argv
[1] == "info":
722 if len(sys
.argv
) == 4:
723 my_type
= sys
.argv
[3]
725 print_caller_info("", func
, my_type
)
726 elif sys
.argv
[1] == "call_info":
727 if len(sys
.argv
) != 4:
729 filename
= sys
.argv
[2]
731 caller_info_values(filename
, func
)
732 print_caller_info(filename
, func
)
733 elif sys
.argv
[1] == "function_ptr" or sys
.argv
[1] == "fn_ptr":
736 elif sys
.argv
[1] == "return_states":
738 print_return_states(func
)
739 print "================================================"
740 print_return_implies(func
)
741 elif sys
.argv
[1] == "return_implies":
743 print_return_implies(func
)
744 elif sys
.argv
[1] == "type_size" or sys
.argv
[1] == "buf_size":
745 struct_type
= sys
.argv
[2]
747 print_type_size(struct_type
, member
)
748 elif sys
.argv
[1] == "data_info":
749 struct_type
= sys
.argv
[2]
751 print_data_info(struct_type
, member
)
752 elif sys
.argv
[1] == "call_tree":
754 print_call_tree(func
)
755 elif sys
.argv
[1] == "find_tagged":
757 param
= int(sys
.argv
[3])
758 find_tagged(func
, param
, 0, [])
759 elif sys
.argv
[1] == "parse_warns_tagged":
760 filename
= sys
.argv
[2]
761 parse_warns_tagged(filename
)
762 elif sys
.argv
[1] == "where":
763 if len(sys
.argv
) == 3:
766 elif len(sys
.argv
) == 4:
767 struct_type
= sys
.argv
[2]
769 function_type_value(struct_type
, member
)
770 elif sys
.argv
[1] == "local":
771 filename
= sys
.argv
[2]
773 if len(sys
.argv
) == 4:
774 variable
= sys
.argv
[3]
775 local_values(filename
, variable
)
776 elif sys
.argv
[1] == "functions":
778 print_functions(member
)
779 elif sys
.argv
[1] == "trace_param":
780 if len(sys
.argv
) != 4:
783 param
= int(sys
.argv
[3])
784 trace_param(func
, param
)
785 elif sys
.argv
[1] == "locals":
786 if len(sys
.argv
) != 3:
788 filename
= sys
.argv
[2]
789 print_locals(filename
);
790 elif sys
.argv
[1] == "constraint":
791 if len(sys
.argv
) == 3:
794 elif len(sys
.argv
) == 4:
795 struct_type
= sys
.argv
[2]
797 constraint(struct_type
, member
)