1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/refinery/analyzers/exception_analyzer.h"
16 :
17 : #include <stdint.h>
18 :
19 : #include <vector>
20 :
21 : #include "base/files/scoped_temp_dir.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/minidump/minidump.h"
24 : #include "syzygy/minidump/unittest_util.h"
25 : #include "syzygy/refinery/unittest_util.h"
26 : #include "syzygy/refinery/analyzers/analyzer_util.h"
27 : #include "syzygy/refinery/analyzers/thread_analyzer.h"
28 : #include "syzygy/refinery/process_state/process_state.h"
29 : #include "syzygy/refinery/process_state/process_state_util.h"
30 : #include "syzygy/refinery/process_state/refinery.pb.h"
31 :
32 m : namespace refinery {
33 :
34 m : TEST(ExceptionAnalyzerTest, AnalyzeMinidump) {
35 m : minidump::FileMinidump minidump;
36 m : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
37 :
38 m : ProcessState process_state;
39 m : SimpleProcessAnalysis analysis(&process_state);
40 :
41 m : ThreadAnalyzer thread_analyzer;
42 m : ASSERT_EQ(Analyzer::ANALYSIS_COMPLETE,
43 m : thread_analyzer.Analyze(minidump, analysis));
44 :
45 m : ExceptionAnalyzer analyzer;
46 m : ASSERT_EQ(Analyzer::ANALYSIS_COMPLETE, analyzer.Analyze(minidump, analysis));
47 :
48 : // Ensure one thread has exception data.
49 m : StackLayerPtr stack_layer;
50 m : ASSERT_TRUE(process_state.FindLayer(&stack_layer));
51 m : for (StackRecordPtr stack : *stack_layer) {
52 m : const Stack& stack_proto = stack->data();
53 m : if (stack_proto.has_thread_info() &&
54 m : stack_proto.thread_info().has_exception())
55 m : return;
56 m : }
57 m : FAIL() << "No exception data found.";
58 m : }
59 :
60 m : class ExceptionAnalyzerSyntheticTest : public testing::SyntheticMinidumpTest {};
61 :
62 m : TEST_F(ExceptionAnalyzerSyntheticTest, NoExceptionTest) {
63 : // Create a minidump with no exception data.
64 m : const char kData[] = "ABCD";
65 m : testing::MinidumpSpecification::MemorySpecification mem_spec(80ULL, kData);
66 m : testing::MinidumpSpecification spec;
67 m : ASSERT_TRUE(spec.AddMemoryRegion(mem_spec));
68 m : ASSERT_NO_FATAL_FAILURE(Serialize(spec));
69 :
70 : // Ensure analysis succeeds.
71 m : minidump::FileMinidump minidump;
72 m : ASSERT_TRUE(minidump.Open(dump_file()));
73 :
74 m : ProcessState process_state;
75 m : SimpleProcessAnalysis analysis(&process_state);
76 m : ExceptionAnalyzer analyzer;
77 m : ASSERT_EQ(Analyzer::ANALYSIS_COMPLETE, analyzer.Analyze(minidump, analysis));
78 m : }
79 :
80 m : TEST_F(ExceptionAnalyzerSyntheticTest, BasicTest) {
81 m : using MemorySpecification =
82 m : testing::MinidumpSpecification::MemorySpecification;
83 m : using ThreadSpecification =
84 m : testing::MinidumpSpecification::ThreadSpecification;
85 m : using ExceptionSpecification =
86 m : testing::MinidumpSpecification::ExceptionSpecification;
87 :
88 : // Generate a synthetic minidump with an exception (as well as thread
89 : // information and backing memory).
90 m : const size_t kThreadId = 42;
91 m : const Address kStackAddr = 80ULL;
92 m : const Address kStackSize = 16U;
93 :
94 m : ThreadSpecification thread_spec(kThreadId, kStackAddr, kStackSize);
95 m : MemorySpecification memory_spec;
96 m : testing::MinidumpSpecification spec;
97 m : thread_spec.FillStackMemorySpecification(&memory_spec);
98 m : ASSERT_TRUE(spec.AddMemoryRegion(memory_spec));
99 m : ASSERT_TRUE(spec.AddThread(thread_spec));
100 m : ExceptionSpecification exception_spec(kThreadId);
101 m : ASSERT_TRUE(spec.AddException(exception_spec));
102 :
103 m : ASSERT_NO_FATAL_FAILURE(Serialize(spec));
104 :
105 : // Analyze.
106 m : minidump::FileMinidump minidump;
107 m : ASSERT_TRUE(minidump.Open(dump_file()));
108 :
109 m : ProcessState process_state;
110 m : SimpleProcessAnalysis analysis(&process_state);
111 m : ThreadAnalyzer thread_analyzer;
112 m : ASSERT_EQ(Analyzer::ANALYSIS_COMPLETE,
113 m : thread_analyzer.Analyze(minidump, analysis));
114 :
115 m : ExceptionAnalyzer analyzer;
116 m : ASSERT_EQ(Analyzer::ANALYSIS_COMPLETE, analyzer.Analyze(minidump, analysis));
117 :
118 : // Validate.
119 m : StackRecordPtr stack_record;
120 m : ASSERT_TRUE(process_state.FindStackRecord(kThreadId, &stack_record));
121 m : const Stack& stack_proto = stack_record->data();
122 m : ASSERT_TRUE(stack_proto.has_thread_info() &&
123 m : stack_proto.thread_info().has_exception());
124 m : const Exception& exception = stack_proto.thread_info().exception();
125 m : ASSERT_EQ(kThreadId, exception.thread_id());
126 m : ASSERT_EQ(exception_spec.exception_code, exception.exception_code());
127 m : ASSERT_EQ(exception_spec.exception_flags, exception.exception_flags());
128 m : ASSERT_EQ(exception_spec.exception_record, exception.exception_record());
129 m : ASSERT_EQ(exception_spec.exception_address, exception.exception_address());
130 m : ASSERT_EQ(exception_spec.exception_information.size(),
131 m : exception.exception_information_size());
132 m : ASSERT_EQ(exception_spec.exception_information[0],
133 m : exception.exception_information(0));
134 m : ASSERT_EQ(exception_spec.exception_information[1],
135 m : exception.exception_information(1));
136 :
137 : // Validate RegisterInformation once implemented.
138 m : const RegisterInformation& reg_info = exception.register_info();
139 m : const CONTEXT* ctx =
140 m : reinterpret_cast<const CONTEXT*>(&exception_spec.context_data.at(0));
141 m : ASSERT_EQ(ctx->SegGs, reg_info.seg_gs());
142 m : ASSERT_EQ(ctx->SegFs, reg_info.seg_fs());
143 m : ASSERT_EQ(ctx->SegEs, reg_info.seg_es());
144 m : ASSERT_EQ(ctx->SegDs, reg_info.seg_ds());
145 m : ASSERT_EQ(ctx->Edi, reg_info.edi());
146 m : ASSERT_EQ(ctx->Esi, reg_info.esi());
147 m : ASSERT_EQ(ctx->Ebx, reg_info.ebx());
148 m : ASSERT_EQ(ctx->Edx, reg_info.edx());
149 m : ASSERT_EQ(ctx->Ecx, reg_info.ecx());
150 m : ASSERT_EQ(ctx->Eax, reg_info.eax());
151 m : ASSERT_EQ(ctx->Ebp, reg_info.ebp());
152 m : ASSERT_EQ(ctx->Eip, reg_info.eip());
153 m : ASSERT_EQ(ctx->SegCs, reg_info.seg_cs());
154 m : ASSERT_EQ(ctx->EFlags, reg_info.eflags());
155 m : ASSERT_EQ(ctx->Esp, reg_info.esp());
156 m : ASSERT_EQ(ctx->SegSs, reg_info.seg_ss());
157 m : }
158 :
159 m : } // namespace refinery
|