Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(278)

Side by Side Diff: snapshot/mac/mach_o_image_annotations_reader_test.cc

Issue 651283003: Add crashpad_info, MachOImageAnnotationsReader, and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Rebase onto master Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « snapshot/mac/mach_o_image_annotations_reader.cc ('k') | snapshot/mac/mach_o_image_reader.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. 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 "snapshot/mac/mach_o_image_annotations_reader.h"
16
17 #include <dlfcn.h>
18 #include <mach/mach.h>
19 #include <signal.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include <map>
25 #include <string>
26 #include <vector>
27
28 #include "base/basictypes.h"
29 #include "client/crashpad_info.h"
30 #include "client/simple_string_dictionary.h"
31 #include "gtest/gtest.h"
32 #include "snapshot/mac/process_reader.h"
33 #include "util/file/fd_io.h"
34 #include "util/mac/mac_util.h"
35 #include "util/mach/exc_server_variants.h"
36 #include "util/mach/exception_ports.h"
37 #include "util/mach/mach_message_server.h"
38 #include "util/test/errors.h"
39 #include "util/test/mac/mach_errors.h"
40 #include "util/test/mac/mach_multiprocess.h"
41
42 namespace crashpad {
43 namespace test {
44 namespace {
45
46 class TestMachOImageAnnotationsReader final : public MachMultiprocess,
47 public UniversalMachExcServer {
48 public:
49 enum TestType {
50 // Don’t crash, just test the CrashpadInfo interface.
51 kDontCrash = 0,
52
53 // The child process should crash by calling abort(). The parent verifies
54 // that the system libraries set the expected annotations.
55 kCrashAbort,
56
57 // The child process should crash by setting DYLD_INSERT_LIBRARIES to
58 // contain a nonexistent library. The parent verifies that dyld sets the
59 // expected annotations.
60 kCrashDyld,
61 };
62
63 explicit TestMachOImageAnnotationsReader(TestType test_type)
64 : MachMultiprocess(),
65 UniversalMachExcServer(),
66 test_type_(test_type) {
67 }
68
69 ~TestMachOImageAnnotationsReader() {}
70
71 // UniversalMachExcServer:
72 kern_return_t CatchMachException(exception_behavior_t behavior,
73 exception_handler_t exception_port,
74 thread_t thread,
75 task_t task,
76 exception_type_t exception,
77 const mach_exception_data_type_t* code,
78 mach_msg_type_number_t code_count,
79 thread_state_flavor_t* flavor,
80 const natural_t* old_state,
81 mach_msg_type_number_t old_state_count,
82 thread_state_t new_state,
83 mach_msg_type_number_t* new_state_count,
84 bool* destroy_complex_request) override {
85 *destroy_complex_request = true;
86
87 EXPECT_EQ(ChildTask(), task);
88
89 ProcessReader process_reader;
90 bool rv = process_reader.Initialize(task);
91 if (!rv) {
92 ADD_FAILURE();
93 } else {
94 const std::vector<ProcessReader::Module>& modules =
95 process_reader.Modules();
96 std::vector<std::string> all_annotations_vector;
97 for (const ProcessReader::Module& module : modules) {
98 MachOImageAnnotationsReader module_annotations_reader(
99 &process_reader, module.reader, module.name);
100 std::vector<std::string> module_annotations_vector =
101 module_annotations_reader.Vector();
102 all_annotations_vector.insert(all_annotations_vector.end(),
103 module_annotations_vector.begin(),
104 module_annotations_vector.end());
105 }
106
107 // Mac OS X 10.6 doesn’t have support for CrashReporter annotations
108 // (CrashReporterClient.h), so don’t look for any special annotations in
109 // that version.
110 int mac_os_x_minor_version = MacOSXMinorVersion();
111 if (mac_os_x_minor_version > 7) {
112 EXPECT_GE(all_annotations_vector.size(), 1u);
113
114 const char* expected_annotation = nullptr;
115 switch (test_type_) {
116 case kCrashAbort:
117 // The child process calls abort(), so the expected annotation
118 // reflects this, with a string set by 10.7.5
119 // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still
120 // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(),
121 // but because abort() tests to see if a message is already set and
122 // something else in Libc will have set a message, this string is
123 // not the expectation on 10.9 or higher. Instead, after fork(), the
124 // child process has a message indicating that a fork() without
125 // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c
126 // _libc_fork_child().
127 expected_annotation =
128 mac_os_x_minor_version <= 8
129 ? "abort() called"
130 : "crashed on child side of fork pre-exec";
131 break;
132
133 case kCrashDyld:
134 // This is independent of dyld’s error_string, which is tested
135 // below.
136 expected_annotation = "dyld: launch, loading dependent libraries";
137 break;
138
139 default:
140 ADD_FAILURE();
141 break;
142 }
143
144 size_t expected_annotation_length = strlen(expected_annotation);
145 bool found = false;
146 for (const std::string& annotation : all_annotations_vector) {
147 // Look for the expectation as a leading susbtring, because the actual
148 // string that dyld uses will have the contents of the
149 // DYLD_INSERT_LIBRARIES environment variable appended to it on Mac
150 // OS X 10.10.
151 if (annotation.substr(0, expected_annotation_length) ==
152 expected_annotation) {
153 found = true;
154 break;
155 }
156 }
157 EXPECT_TRUE(found);
158 }
159
160 // dyld exposes its error_string at least as far back as Mac OS X 10.4.
161 if (test_type_ == kCrashDyld) {
162 const char kExpectedAnnotation[] = "could not load inserted library";
163 size_t expected_annotation_length = strlen(kExpectedAnnotation);
164 bool found = false;
165 for (const std::string& annotation : all_annotations_vector) {
166 // Look for the expectation as a leading substring, because the actual
167 // string will contain the library’s pathname and, on Mac OS X 10.9
168 // and later, a reason.
169 if (annotation.substr(0, expected_annotation_length) ==
170 kExpectedAnnotation) {
171 found = true;
172 break;
173 }
174 }
175
176 EXPECT_TRUE(found);
177 }
178 }
179
180 return ExcServerSuccessfulReturnValue(behavior, false);
181 }
182
183 private:
184 // MachMultiprocess:
185
186 void MachMultiprocessParent() override {
187 ProcessReader process_reader;
188 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
189
190 // Wait for the child process to indicate that it’s done setting up its
191 // annotations via the CrashpadInfo interface.
192 char c;
193 CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
194
195 // Verify the “simple map” annotations set via the CrashpadInfo interface.
196 const std::vector<ProcessReader::Module>& modules =
197 process_reader.Modules();
198 std::map<std::string, std::string> all_annotations_simple_map;
199 for (const ProcessReader::Module& module : modules) {
200 MachOImageAnnotationsReader module_annotations_reader(
201 &process_reader, module.reader, module.name);
202 std::map<std::string, std::string> module_annotations_simple_map =
203 module_annotations_reader.SimpleMap();
204 all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
205 module_annotations_simple_map.end());
206 }
207
208 EXPECT_GE(all_annotations_simple_map.size(), 5u);
209 EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
210 EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
211 EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]);
212 EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]);
213 EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
214
215 // Tell the child process that it’s permitted to crash.
216 CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
217
218 if (test_type_ != kDontCrash) {
219 // Handle the child’s crash. Further validation will be done in
220 // CatchMachException().
221 mach_msg_return_t mr =
222 MachMessageServer::Run(this,
223 LocalPort(),
224 MACH_MSG_OPTION_NONE,
225 MachMessageServer::kOneShot,
226 MachMessageServer::kBlocking,
227 MACH_MSG_TIMEOUT_NONE);
228 EXPECT_EQ(MACH_MSG_SUCCESS, mr)
229 << MachErrorMessage(mr, "MachMessageServer::Run");
230
231 switch (test_type_) {
232 case kCrashAbort:
233 SetExpectedChildTermination(kTerminationSignal, SIGABRT);
234 break;
235
236 case kCrashDyld:
237 // dyld fatal errors result in the execution of an int3 instruction on
238 // x86 and a trap instruction on ARM, both of which raise SIGTRAP.
239 // 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error.
240 SetExpectedChildTermination(kTerminationSignal, SIGTRAP);
241 break;
242
243 default:
244 FAIL();
245 break;
246 }
247 }
248 }
249
250 void MachMultiprocessChild() override {
251 CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
252
253 // This is “leaked” to crashpad_info.
254 SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();
255 simple_annotations->SetKeyValue("#TEST# pad", "break");
256 simple_annotations->SetKeyValue("#TEST# key", "value");
257 simple_annotations->SetKeyValue("#TEST# pad", "crash");
258 simple_annotations->SetKeyValue("#TEST# x", "y");
259 simple_annotations->SetKeyValue("#TEST# longer", "shorter");
260 simple_annotations->SetKeyValue("#TEST# empty_value", "");
261
262 crashpad_info->set_simple_annotations(simple_annotations);
263
264 // Tell the parent that the environment has been set up.
265 char c = '\0';
266 CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
267
268 // Wait for the parent to indicate that it’s safe to crash.
269 CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
270
271 // Direct an exception message to the exception server running in the
272 // parent.
273 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask,
274 mach_task_self());
275 ASSERT_TRUE(exception_ports.SetExceptionPort(
276 EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE));
277
278 switch (test_type_) {
279 case kDontCrash:
280 break;
281
282 case kCrashAbort:
283 abort();
284 break;
285
286 case kCrashDyld: {
287 // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist.
288 // Unable to load it, dyld will abort with a fatal error.
289 ASSERT_EQ(
290 0,
291 setenv(
292 "DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1))
293 << ErrnoMessage("setenv");
294
295 // The actual executable doesn’t matter very much, because dyld won’t
296 // ever launch it. It just needs to be an executable that uses dyld as
297 // its LC_LOAD_DYLINKER (all normal executables do). /usr/bin/true is on
298 // every system, so use it.
299 ASSERT_EQ(0, execl("/usr/bin/true", "true", nullptr))
300 << ErrnoMessage("execl");
301 break;
302 }
303
304 default:
305 break;
306 }
307 }
308
309 TestType test_type_;
310
311 DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader);
312 };
313
314 TEST(MachOImageAnnotationsReader, DontCrash) {
315 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
316 TestMachOImageAnnotationsReader::kDontCrash);
317 test_mach_o_image_annotations_reader.Run();
318 }
319
320 TEST(MachOImageAnnotationsReader, CrashAbort) {
321 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
322 TestMachOImageAnnotationsReader::kCrashAbort);
323 test_mach_o_image_annotations_reader.Run();
324 }
325
326 TEST(MachOImageAnnotationsReader, CrashDyld) {
327 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
328 TestMachOImageAnnotationsReader::kCrashDyld);
329 test_mach_o_image_annotations_reader.Run();
330 }
331
332 } // namespace
333 } // namespace test
334 } // namespace crashpad
OLDNEW
« no previous file with comments | « snapshot/mac/mach_o_image_annotations_reader.cc ('k') | snapshot/mac/mach_o_image_reader.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698