diff --git a/infer/src/harness/stacktrace.ml b/infer/src/harness/stacktrace.ml new file mode 100644 index 000000000..149e58960 --- /dev/null +++ b/infer/src/harness/stacktrace.ml @@ -0,0 +1,61 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +(** Module for parsing stack traces and using them to guide Infer analysis *) + +module F = Format + +type frame = { + class_str : string; + method_str : string; + file_str : string; + line_num : int; +} + +type t = { + exception_name: string; + frames: frame list; +} + +let make exception_name frames = { exception_name; frames; } + +let make_frame class_str method_str file_str line_num = + { class_str; method_str; file_str; line_num; } + +let parse_stack_frame frame_str = + (* separate the qualified method name and the parenthesized text/line number*) + ignore(Str.string_match (Str.regexp "\t*at \\(.*\\)(\\(.*\\))") frame_str 0); + let qualified_procname = Str.matched_group 1 frame_str in + let file_and_line = Str.matched_group 2 frame_str in + (* separate the class name from the method name *) + ignore(Str.string_match (Str.regexp "\\(.*\\)\\.\\(.*\\)") qualified_procname 0); + let class_str = Str.matched_group 1 qualified_procname in + let method_str = Str.matched_group 2 qualified_procname in + (* separate the filename and line number *) + ignore(Str.string_match (Str.regexp "\\(.*\\):\\([0-9]+\\)") file_and_line 0); + let file_str = Str.matched_group 1 file_and_line in + let line_num = int_of_string (Str.matched_group 2 file_and_line) in + make_frame class_str method_str file_str line_num + +let parse_exception_line exception_line = + ignore(Str.string_match + (Str.regexp "Exception in thread \"\\(.*\\)\" \\(.*\\)") + exception_line + 0); + let exception_name = Str.matched_group 2 exception_line in + exception_name + +let of_string s = + let lines = Str.split (Str.regexp "\n") s in + match lines with + | exception_line :: trace -> + let exception_name = parse_exception_line exception_line in + let parsed = IList.map parse_stack_frame trace in + make exception_name parsed + | [] -> failwith "Empty stack trace" diff --git a/infer/src/harness/stacktrace.mli b/infer/src/harness/stacktrace.mli new file mode 100644 index 000000000..65ec3e0ff --- /dev/null +++ b/infer/src/harness/stacktrace.mli @@ -0,0 +1,28 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +(** Module for parsing stack traces and using them to guide Infer analysis *) + +type frame = { + class_str : string; + method_str : string; + file_str : string; + line_num : int; +} + +type t = { + exception_name: string; + frames: frame list; +} + +val make : string -> frame list -> t + +val make_frame : string -> string -> string -> int -> frame + +val of_string : string -> t diff --git a/infer/src/unit/inferunit.ml b/infer/src/unit/inferunit.ml index 957f31b84..8a0107aea 100644 --- a/infer/src/unit/inferunit.ml +++ b/infer/src/unit/inferunit.ml @@ -23,6 +23,7 @@ let () = LivenessTests.tests; SchedulerTests.tests; BoundedCallTreeTests.tests; + StacktraceTests.tests; ] in let test_suite = "all" >::: tests in OUnit2.run_test_tt_main test_suite diff --git a/infer/src/unit/stacktraceTests.ml b/infer/src/unit/stacktraceTests.ml new file mode 100644 index 000000000..6ce2cd2fd --- /dev/null +++ b/infer/src/unit/stacktraceTests.ml @@ -0,0 +1,73 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open !Utils + +module F = Format + +let tests = + let open OUnit2 in + + let empty_string_test = + let empty_string_test_ _ = + assert_raises + (Failure "Empty stack trace") + (fun () -> Stacktrace.of_string "") in + "empty_string">::empty_string_test_ in + + let empty_trace_test = + let empty_stack_trace_s = "Exception in thread \"main\" java.lang.NullPointerException" in + let trace = Stacktrace.of_string empty_stack_trace_s in + let empty_trace_test_ _ = + assert_equal trace.frames [] in + "empty_trace">::empty_trace_test_ in + + let one_frame_trace_test = + let one_frame_trace_test_s = + "Exception in thread \"main\" java.lang.NullPointerException\n" ^ + "\tat endtoend.java.checkers.crashcontext.MinimalCrashTest.main" ^ + "(MinimalCrashTest.java:16)" in + let trace = Stacktrace.of_string + one_frame_trace_test_s in + let expected = Stacktrace.make + "java.lang.NullPointerException" + [Stacktrace.make_frame + "endtoend.java.checkers.crashcontext.MinimalCrashTest" + "main" + "MinimalCrashTest.java" + 16] in + let one_frame_trace_test_ _ = + assert_equal trace expected in + "one_frame_trace">::one_frame_trace_test_ in + + let multi_frame_trace_test = + let multi_frame_trace_test_s = + "Exception in thread \"main\" java.lang.NullPointerException\n\t" ^ + "at endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest.bar" ^ + "(MultiStackFrameCrashTest.java:16)\n" ^ + "\tat endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest.foo" ^ + "(MultiStackFrameCrashTest.java:20)\n" ^ + "\tat endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest.main" ^ + "(MultiStackFrameCrashTest.java:24)" in + let trace = Stacktrace.of_string + multi_frame_trace_test_s in + let class_name = + "endtoend.java.checkers.crashcontext.MultiStackFrameCrashTest" in + let file_name = "MultiStackFrameCrashTest.java" in + let expected = Stacktrace.make + "java.lang.NullPointerException" + [Stacktrace.make_frame class_name "bar" file_name 16; + Stacktrace.make_frame class_name "foo" file_name 20; + Stacktrace.make_frame class_name "main" file_name 24] in + let multi_frame_trace_test_ _ = + assert_equal trace expected in + "multi_frame_trace_test">::multi_frame_trace_test_ in + + "all_tests_suite">:::[empty_string_test; empty_trace_test; + one_frame_trace_test; multi_frame_trace_test]