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/bard/story.h"
16 :
17 : #include <vector>
18 :
19 : #include "base/atomicops.h"
20 : #include "gmock/gmock.h"
21 : #include "gtest/gtest.h"
22 : #include "syzygy/bard/events/heap_alloc_event.h"
23 : #include "syzygy/bard/events/heap_create_event.h"
24 : #include "syzygy/bard/events/heap_destroy_event.h"
25 : #include "syzygy/bard/events/heap_free_event.h"
26 : #include "syzygy/bard/events/heap_size_event.h"
27 : #include "syzygy/bard/events/linked_event.h"
28 : #include "syzygy/core/unittest_util.h"
29 :
30 : namespace bard {
31 : namespace {
32 :
33 : using events::LinkedEvent;
34 :
35 : using events::HeapAllocEvent;
36 : using events::HeapCreateEvent;
37 : using events::HeapDestroyEvent;
38 : using events::HeapFreeEvent;
39 : using events::HeapSizeEvent;
40 :
41 : const HANDLE kLiveHeap = reinterpret_cast<HANDLE>(0x4197FC83);
42 : const HANDLE kTraceHeap = reinterpret_cast<HANDLE>(0xAB12CD34);
43 : const LPVOID kLiveAlloc = reinterpret_cast<LPVOID>(0x4820BC7A);
44 : const LPVOID kTraceAlloc = reinterpret_cast<LPVOID>(0xF1D97AE4);
45 : const DWORD kFlags = 1;
46 : const DWORD kOptions = 0;
47 : const SIZE_T kBytes = 100;
48 : const SIZE_T kSize = 100;
49 : const SIZE_T kInitialSize = 1;
50 : const SIZE_T kMaximumSize = 1000;
51 :
52 : class InvalidEvent : public EventInterface {
53 : public:
54 : EventType type() const override { return EventType::kMaxEventType; }
55 :
56 : bool Play(void* backdrop) override { return true; }
57 : bool Equals(const EventInterface*) const override { return false; }
58 : };
59 :
60 : // A simple event that simply returns false. Used for testing playback.
61 : class FailedEvent : public EventInterface {
62 : public:
63 i : EventType type() const override { return EventType::kMaxEventType; }
64 :
65 E : bool Play(void* backdrop) override { return false; }
66 i : bool Equals(const EventInterface*) const override { return false; }
67 : };
68 :
69 : // A simple event that appends its ID to a vector. Used for testing playback.
70 : class AppendEvent : public EventInterface {
71 : public:
72 E : explicit AppendEvent(uint32_t id) : id_(id) {}
73 i : EventType type() const override { return EventType::kMaxEventType; }
74 :
75 E : bool Play(void* backdrop) override {
76 E : auto v = reinterpret_cast<std::vector<uint32_t>*>(backdrop);
77 E : v->push_back(id_);
78 E : return true;
79 E : }
80 i : bool Equals(const EventInterface*) const override { return false; }
81 :
82 : private:
83 : uint32_t id_;
84 : };
85 :
86 : // A simple event that increments a counter atomically. Used for testing
87 : // playback.
88 : class IncrementEvent : public EventInterface {
89 : public:
90 E : explicit IncrementEvent(uint32_t amount) : amount_(amount) {}
91 i : EventType type() const override { return EventType::kMaxEventType; }
92 :
93 E : bool Play(void* backdrop) override {
94 E : auto atomic = reinterpret_cast<volatile base::subtle::Atomic32*>(backdrop);
95 E : base::subtle::Barrier_AtomicIncrement(atomic, amount_);
96 E : return true;
97 E : }
98 i : bool Equals(const EventInterface*) const override { return false; }
99 :
100 : private:
101 : uint32_t amount_;
102 : };
103 :
104 : } // namespace
105 :
106 E : TEST(StoryTest, CreatePlotLine) {
107 E : Story s;
108 E : EXPECT_EQ(0u, s.plot_lines().size());
109 E : auto pl = s.CreatePlotLine();
110 E : EXPECT_TRUE(pl);
111 E : EXPECT_EQ(1u, s.plot_lines().size());
112 :
113 E : Story::PlotLine* pl2 = new Story::PlotLine();
114 E : EXPECT_EQ(pl2, s.AddPlotLine(std::unique_ptr<Story::PlotLine>(pl2)));
115 E : EXPECT_EQ(2u, s.plot_lines().size());
116 E : }
117 :
118 E : TEST(StoryTest, TestSerialization) {
119 E : std::unique_ptr<EventInterface> event1(
120 : new HeapCreateEvent(0, kOptions, kInitialSize, kMaximumSize, kTraceHeap));
121 E : std::unique_ptr<EventInterface> event2(
122 : new HeapAllocEvent(0, kTraceHeap, kFlags, kBytes, kTraceAlloc));
123 E : std::unique_ptr<EventInterface> event3(
124 : new HeapSizeEvent(0, kTraceHeap, kFlags, kTraceAlloc, kSize));
125 E : std::unique_ptr<EventInterface> event4(
126 : new HeapFreeEvent(0, kTraceHeap, kFlags, kTraceAlloc, true));
127 E : std::unique_ptr<EventInterface> event5(
128 : new HeapDestroyEvent(0, kTraceHeap, true));
129 :
130 : // The following events will either be cross plot line dependencies or have
131 : // such dependencies.
132 E : std::unique_ptr<LinkedEvent> linked_event1(
133 : new LinkedEvent(std::move(event1)));
134 E : std::unique_ptr<LinkedEvent> linked_event2(
135 : new LinkedEvent(std::move(event2)));
136 E : std::unique_ptr<LinkedEvent> linked_event4(
137 : new LinkedEvent(std::move(event4)));
138 E : std::unique_ptr<LinkedEvent> linked_event5(
139 : new LinkedEvent(std::move(event5)));
140 :
141 : // Alloc depends on Create, as it would be on another thread.
142 E : linked_event2->AddDep(linked_event1.get());
143 :
144 : // Similarly the heap can't be detroyed until all use of it has been
145 : // completed.
146 E : linked_event5->AddDep(linked_event4.get());
147 :
148 E : std::unique_ptr<Story::PlotLine> plot_line1(new Story::PlotLine());
149 E : std::unique_ptr<Story::PlotLine> plot_line2(new Story::PlotLine());
150 :
151 : // One plot line creates and frees the heap.
152 E : plot_line1->push_back(linked_event1.release());
153 E : plot_line1->push_back(linked_event5.release());
154 :
155 : // Another plot line owns the allocation.
156 E : plot_line2->push_back(linked_event2.release());
157 E : plot_line2->push_back(event3.release());
158 E : plot_line2->push_back(linked_event4.release());
159 :
160 : // Create a story to wrap it all up.
161 E : Story story;
162 E : story.AddPlotLine(std::move(plot_line1));
163 E : story.AddPlotLine(std::move(plot_line2));
164 :
165 E : EXPECT_TRUE(testing::TestSerialization(story));
166 E : }
167 :
168 E : TEST(PlotLineRunnerTest, StopOnFailedEvent) {
169 E : Story::PlotLine plot_line;
170 E : plot_line.push_back(new AppendEvent(0));
171 E : plot_line.push_back(new FailedEvent());
172 E : plot_line.push_back(new AppendEvent(1));
173 :
174 E : std::vector<uint32_t> v;
175 E : Story::PlotLineRunner runner(&v, &plot_line);
176 E : runner.Start();
177 E : runner.Join();
178 :
179 E : EXPECT_TRUE(runner.Failed());
180 E : EXPECT_EQ(plot_line[1], runner.failed_event());
181 E : EXPECT_THAT(v, testing::ElementsAre(0));
182 E : }
183 :
184 E : TEST(PlotLineRunnerTest, Succeeds) {
185 E : Story::PlotLine plot_line;
186 E : plot_line.push_back(new AppendEvent(0));
187 E : plot_line.push_back(new AppendEvent(1));
188 E : plot_line.push_back(new AppendEvent(2));
189 :
190 E : std::vector<uint32_t> v;
191 E : Story::PlotLineRunner runner(&v, &plot_line);
192 E : runner.Start();
193 E : runner.Join();
194 :
195 E : EXPECT_FALSE(runner.Failed());
196 E : EXPECT_EQ(nullptr, runner.failed_event());
197 E : EXPECT_THAT(v, testing::ElementsAre(0, 1, 2));
198 E : }
199 :
200 E : TEST(StoryTest, PlaybackStopsAndFails) {
201 E : Story story;
202 :
203 E : auto plot_line = story.CreatePlotLine();
204 E : plot_line->push_back(new AppendEvent(0));
205 E : plot_line->push_back(new FailedEvent());
206 E : plot_line->push_back(new AppendEvent(1));
207 :
208 E : std::vector<uint32_t> v;
209 E : EXPECT_FALSE(story.Play(&v));
210 E : EXPECT_THAT(v, testing::ElementsAre(0));
211 E : }
212 :
213 E : TEST(StoryTest, PlaybackSucceeds) {
214 E : Story story;
215 :
216 : // 10 plotlines (threads) with 10000 events each was sufficient to generate
217 : // race conditions on a Z600.
218 E : uint32_t sum = 0;
219 E : for (size_t i = 0; i < 10; ++i) {
220 E : auto pl = story.CreatePlotLine();;
221 E : for (size_t j = 0; j < 10000; ++j) {
222 E : pl->push_back(new IncrementEvent(j + i));
223 E : sum += j + i;
224 E : }
225 E : }
226 :
227 E : base::subtle::Atomic32 atomic = 0;
228 E : EXPECT_TRUE(story.Play(&atomic));
229 E : EXPECT_EQ(sum, atomic);
230 E : }
231 :
232 : } // namespace bard
|