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/heap_analyzer.h"
16 :
17 : #include "gtest/gtest.h"
18 : #include "syzygy/common/unittest_util.h"
19 : #include "syzygy/refinery/unittest_util.h"
20 : #include "syzygy/refinery/analyzers/analysis_runner.h"
21 : #include "syzygy/refinery/analyzers/analyzer_util.h"
22 : #include "syzygy/refinery/analyzers/memory_analyzer.h"
23 : #include "syzygy/refinery/analyzers/module_analyzer.h"
24 : #include "syzygy/refinery/process_state/process_state.h"
25 : #include "syzygy/refinery/process_state/process_state_util.h"
26 : #include "syzygy/refinery/symbols/symbol_provider.h"
27 :
28 m : namespace refinery {
29 :
30 m : namespace {
31 :
32 m : bool AnalyzeMinidump(const base::FilePath& minidump_path,
33 m : ProcessState* process_state) {
34 m : minidump::FileMinidump minidump;
35 m : if (!minidump.Open(minidump_path))
36 m : return false;
37 :
38 m : AnalysisRunner runner;
39 m : runner.AddAnalyzer(
40 m : std::move(std::unique_ptr<Analyzer>(new refinery::MemoryAnalyzer())));
41 m : runner.AddAnalyzer(
42 m : std::move(std::unique_ptr<Analyzer>(new refinery::ModuleAnalyzer())));
43 m : runner.AddAnalyzer(
44 m : std::move(std::unique_ptr<Analyzer>(new refinery::HeapAnalyzer())));
45 :
46 m : SimpleProcessAnalysis analysis(process_state);
47 m : analysis.set_symbol_provider(new SymbolProvider());
48 :
49 m : return runner.Analyze(minidump, analysis) == Analyzer::ANALYSIS_COMPLETE;
50 m : }
51 :
52 m : class HeapAnalyzerTest : public testing::Test {
53 m : public:
54 m : void SetUp() override {
55 m : ASSERT_TRUE(scoped_symbol_path_.Setup());
56 m : }
57 :
58 m : private:
59 m : testing::ScopedSymbolPath scoped_symbol_path_;
60 m : };
61 :
62 m : } // namespace
63 :
64 m : TEST_F(HeapAnalyzerTest, AnalyzeHeap) {
65 m : if (testing::IsAppVerifierActive()) {
66 m : LOG(WARNING) << "HeapAnalyzerTest.AnalyzeHeap is incompatible with AV.";
67 m : return;
68 m : }
69 :
70 m : testing::ScopedMinidump minidump;
71 m : testing::ScopedHeap heap;
72 :
73 m : ASSERT_TRUE(heap.Create());
74 :
75 m : const size_t kBlockSize = 19;
76 m : void* lfh_block = nullptr;
77 m : void* free_lfh_block = nullptr;
78 m : for (size_t tries = 0; tries < 1000 && !lfh_block; ++tries) {
79 m : void* block = heap.Allocate(kBlockSize);
80 m : if (heap.IsLFHBlock(block)) {
81 : // Grab one block to free later first.
82 m : if (free_lfh_block == nullptr)
83 m : free_lfh_block = block;
84 m : else
85 m : lfh_block = block;
86 m : }
87 m : }
88 m : ASSERT_TRUE(free_lfh_block);
89 m : ASSERT_TRUE(lfh_block);
90 m : heap.Free(free_lfh_block);
91 :
92 m : ASSERT_TRUE(
93 m : minidump.GenerateMinidump(testing::ScopedMinidump::kMinidumpWithData));
94 m : ProcessState process_state;
95 m : ASSERT_TRUE(AnalyzeMinidump(minidump.minidump_path(), &process_state));
96 :
97 : // Find the lfh_block allocation.
98 m : HeapAllocationLayerPtr alloc_layer;
99 m : ASSERT_TRUE(process_state.FindLayer(&alloc_layer));
100 m : std::vector<HeapAllocationRecordPtr> alloc_records;
101 m : alloc_layer->GetRecordsAt(testing::ToAddress(lfh_block), &alloc_records);
102 m : ASSERT_EQ(1U, alloc_records.size());
103 m : ASSERT_EQ(kBlockSize, alloc_records[0]->range().size());
104 m : ASSERT_FALSE(alloc_records[0]->data().is_free());
105 :
106 : // Find the free_lfh_block allocation.
107 m : alloc_layer->GetRecordsAt(testing::ToAddress(free_lfh_block), &alloc_records);
108 m : ASSERT_EQ(1U, alloc_records.size());
109 m : ASSERT_LE(kBlockSize, alloc_records[0]->range().size());
110 m : ASSERT_TRUE(alloc_records[0]->data().is_free());
111 :
112 : // Find the heap entry preceding the allocation.
113 m : HeapMetadataLayerPtr heap_meta_layer;
114 m : ASSERT_TRUE(process_state.FindLayer(&heap_meta_layer));
115 m : std::vector<HeapMetadataRecordPtr> heap_meta_records;
116 m : heap_meta_layer->GetRecordsIntersecting(
117 m : AddressRange(testing::ToAddress(lfh_block) - 1, 1), &heap_meta_records);
118 m : ASSERT_EQ(1U, heap_meta_records.size());
119 m : ASSERT_FALSE(heap_meta_records[0]->data().corrupt());
120 :
121 : // Find the heap entry preceding the freed allocation.
122 m : heap_meta_layer->GetRecordsIntersecting(
123 m : AddressRange(testing::ToAddress(free_lfh_block) - 1, 1),
124 m : &heap_meta_records);
125 m : ASSERT_EQ(1U, heap_meta_records.size());
126 m : ASSERT_FALSE(heap_meta_records[0]->data().corrupt());
127 m : }
128 :
129 : // TODO(siggi): Test corruption etc.
130 :
131 m : } // namespace refinery
|