OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 Google Inc. | |
2 // All rights reserved. | |
3 // | |
4 // Redistribution and use in source and binary forms, with or without | |
5 // modification, are permitted provided that the following conditions are | |
6 // met: | |
7 // | |
8 // * Redistributions of source code must retain the above copyright | |
9 // notice, this list of conditions and the following disclaimer. | |
10 // * Redistributions in binary form must reproduce the above | |
11 // copyright notice, this list of conditions and the following disclaimer | |
12 // in the documentation and/or other materials provided with the | |
13 // distribution. | |
14 // * Neither the name of Google Inc. nor the names of its | |
15 // contributors may be used to endorse or promote products derived from | |
16 // this software without specific prior written permission. | |
17 // | |
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | |
30 // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | |
31 | |
32 // stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader. | |
33 | |
34 #include <a.out.h> | |
35 #include <cassert> | |
36 #include <cerrno> | |
37 #include <cstdarg> | |
38 #include <cstdlib> | |
39 #include <cstring> | |
40 #include <fstream> | |
41 #include <iomanip> | |
42 #include <iostream> | |
43 #include <map> | |
44 #include <sstream> | |
45 #include <stab.h> | |
46 | |
47 #include "breakpad_googletest_includes.h" | |
48 #include "common/stabs_reader.h" | |
49 | |
50 using std::istream; | |
51 using std::istringstream; | |
52 using std::map; | |
53 using std::ostream; | |
54 using std::ostringstream; | |
55 using std::string; | |
56 | |
57 using ::testing::_; | |
58 using ::testing::Eq; | |
59 using ::testing::InSequence; | |
60 using ::testing::Return; | |
61 using ::testing::Sequence; | |
62 using ::testing::StrEq; | |
63 | |
64 using google_breakpad::StabsHandler; | |
65 using google_breakpad::StabsReader; | |
66 | |
67 namespace { | |
68 | |
69 // Mock stabs file parser | |
70 // | |
71 // In order to test StabsReader, we parse a human-readable input file | |
72 // describing STABS entries into in-memory .stab and .stabstr | |
73 // sections, and then pass those to StabsReader to look at. The | |
74 // human-readable file is called a "mock stabs file". | |
75 // | |
76 // Except for compilation unit boundary lines (described below), each | |
77 // line of a mock stabs file should have the following form: | |
78 // | |
79 // TYPE OTHER DESC VALUE NAME | |
80 // | |
81 // where all data is Latin-1 bytes and fields are separated by single | |
82 // space characters, except for NAME, which may contain spaces and | |
83 // continues to the end of the line. The fields have the following | |
84 // meanings: | |
85 // | |
86 // - TYPE: the name of the stabs symbol type; like SO or FUN. These are | |
87 // the names from /usr/include/bits/stab.def, without the leading N_. | |
88 // | |
89 // - OTHER, DESC, VALUE: numeric values for the n_other, n_desc, and | |
90 // n_value fields of the stab. These can be decimal or hex, | |
91 // using C++ notation (10, 0x10) | |
92 // | |
93 // - NAME: textual data for the entry. STABS packs all kinds of | |
94 // interesting data into entries' NAME fields, so calling it a NAME | |
95 // is misleading, but that's how it is. For SO, this may be a | |
96 // filename; for FUN, this is the function name, plus type data; and | |
97 // so on. | |
98 // | |
99 // A compilation unit boundary line has the form: | |
100 // | |
101 // cu-boundary FILENAME | |
102 | |
103 // I don't know if the whole parser/handler pattern is really worth | |
104 // the bureaucracy in this case. But just writing it out as | |
105 // old-fashioned functions wasn't astonishingly clear either, so it | |
106 // seemed worth a try. | |
107 | |
108 // A handler class for mock stabs data. | |
109 class MockStabsHandler { | |
110 public: | |
111 MockStabsHandler() { } | |
112 virtual ~MockStabsHandler() { } | |
113 // The mock stabs parser calls this member function for each entry | |
114 // it parses, passing it the contents of the entry. If this function | |
115 // returns true, the parser continues; if it returns false, the parser | |
116 // stops, and its Process member function returns false. | |
117 virtual bool Entry(enum __stab_debug_code type, char other, short desc, | |
118 unsigned long value, const string &name) { return true; } | |
119 // Report a compilation unit boundary whose filename is FILENAME. As | |
120 // for the Entry function, this should return true to continue | |
121 // parsing, or false to stop processing. | |
122 virtual bool CUBoundary(const string &filename) { return true; } | |
123 | |
124 // Report an error in parsing the mock stabs data. If this returns true, | |
125 // the parser continues; if it returns false, the parser stops and | |
126 // its Process member function returns false. | |
127 virtual bool Error(const char *format, ...) = 0; | |
128 }; | |
129 | |
130 // A class for parsing mock stabs files. | |
131 class MockStabsParser { | |
132 public: | |
133 // Create a parser reading input from STREAM and passing data to HANDLER. | |
134 // Use FILENAME when reporting errors. | |
135 MockStabsParser(const string &filename, istream *stream, | |
136 MockStabsHandler *handler); | |
137 // Parse data from the STREAM, invoking HANDLER->Entry for each | |
138 // entry we get. Return true if we parsed all the data succesfully, | |
139 // or false if we stopped early because Entry returned false, or if | |
140 // there were any errors during parsing. | |
141 bool Process(); | |
142 private: | |
143 // A type for maps from stab type names ("SO", "SLINE", etc.) to | |
144 // n_type values. | |
145 typedef map<string, unsigned char> StabTypeNameTable; | |
146 | |
147 // Initialize the table mapping STAB type names to n_type values. | |
148 void InitializeTypeNames(); | |
149 | |
150 // Parse LINE, one line of input from a mock stabs file, and pass | |
151 // its contents to handler_->Entry and return the boolean value that | |
152 // returns. If we encounter an error parsing the line, report it | |
153 // using handler->Error. | |
154 bool ParseLine(const string &line); | |
155 | |
156 const string &filename_; | |
157 istream *stream_; | |
158 MockStabsHandler *handler_; | |
159 int line_number_; | |
160 StabTypeNameTable type_names_; | |
161 }; | |
162 | |
163 MockStabsParser::MockStabsParser(const string &filename, istream *stream, | |
164 MockStabsHandler *handler): | |
165 filename_(filename), stream_(stream), handler_(handler), | |
166 line_number_(0) { | |
167 InitializeTypeNames(); | |
168 } | |
169 | |
170 bool MockStabsParser::Process() { | |
171 // Iterate once per line, including a line at EOF without a | |
172 // terminating newline. | |
173 for(;;) { | |
174 string line; | |
175 getline(*stream_, line, '\n'); | |
176 if (line.empty() && stream_->eof()) | |
177 break; | |
178 line_number_++; | |
179 if (! ParseLine(line)) | |
180 return false; | |
181 } | |
182 return true; | |
183 } | |
184 | |
185 void MockStabsParser::InitializeTypeNames() { | |
186 // On GLIBC-based systems, <bits/stab.def> is a file containing a | |
187 // call to an unspecified macro __define_stab for each stab type. | |
188 // <stab.h> uses it to define the __stab_debug_code enum type. We | |
189 // use it here to initialize our mapping from type names to enum | |
190 // values. | |
191 // | |
192 // This isn't portable to non-GLIBC systems. Feel free to just | |
193 // hard-code the values if this becomes a problem. | |
194 # define __define_stab(name, code, str) type_names_[string(str)] = code; | |
195 # include <bits/stab.def> | |
196 # undef __define_stab | |
197 } | |
198 | |
199 bool MockStabsParser::ParseLine(const string &line) { | |
200 istringstream linestream(line); | |
201 // Allow "0x" prefix for hex, and so on. | |
202 linestream.unsetf(istringstream::basefield); | |
203 // Parse and validate the stabs type. | |
204 string typeName; | |
205 linestream >> typeName; | |
206 if (typeName == "cu-boundary") { | |
207 if (linestream.peek() == ' ') | |
208 linestream.get(); | |
209 string filename; | |
210 getline(linestream, filename, '\n'); | |
211 return handler_->CUBoundary(filename); | |
212 } else { | |
213 StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName); | |
214 if (typeIt == type_names_.end()) | |
215 return handler_->Error("%s:%d: unrecognized stab type: %s\n", | |
216 filename_.c_str(), line_number_, typeName.c_str()); | |
217 // These are int, not char and unsigned char, to ensure they're parsed | |
218 // as decimal numbers, not characters. | |
219 int otherInt, descInt; | |
220 unsigned long value; | |
221 linestream >> otherInt >> descInt >> value; | |
222 if (linestream.fail()) | |
223 return handler_->Error("%s:%d: malformed mock stabs input line\n", | |
224 filename_.c_str(), line_number_); | |
225 if (linestream.peek() == ' ') | |
226 linestream.get(); | |
227 string name; | |
228 getline(linestream, name, '\n'); | |
229 return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second), | |
230 otherInt, descInt, value, name); | |
231 } | |
232 } | |
233 | |
234 // A class for constructing .stab sections. | |
235 // | |
236 // A .stab section is an array of struct nlist entries. These | |
237 // entries' n_un.n_strx fields are indices into an accompanying | |
238 // .stabstr section. | |
239 class StabSection { | |
240 public: | |
241 StabSection(): used_(0), size_(1) { | |
242 entries_ = (struct nlist *) malloc(sizeof(*entries_) * size_); | |
243 } | |
244 ~StabSection() { free(entries_); } | |
245 | |
246 // Append a new 'struct nlist' entry to the end of the section, and | |
247 // return a pointer to it. This pointer is valid until the next | |
248 // call to Append. The caller should initialize the returned entry | |
249 // as needed. | |
250 struct nlist *Append(); | |
251 // Set the first entry's n_desc field to COUNT, and set its n_value field | |
252 // to STRING_SIZE. | |
253 void SetHeader(short count, unsigned long string_size); | |
254 // Set SECTION to the contents of a .stab section holding the | |
255 // accumulated list of entries added with Append. | |
256 void GetSection(string *section); | |
257 // Clear the array, and prepare this StabSection to accumulate a fresh | |
258 // set of entries. | |
259 void Clear(); | |
260 | |
261 private: | |
262 // The array of stabs entries, | |
263 struct nlist *entries_; | |
264 // The number of elements of entries_ that are used, and the allocated size | |
265 // of the array. | |
266 size_t used_, size_; | |
267 }; | |
268 | |
269 struct nlist *StabSection::Append() { | |
270 if (used_ == size_) { | |
271 size_ *= 2; | |
272 entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_); | |
273 } | |
274 assert(used_ < size_); | |
275 return &entries_[used_++]; | |
276 } | |
277 | |
278 void StabSection::SetHeader(short count, unsigned long string_size) { | |
279 assert(used_ >= 1); | |
280 entries_[0].n_desc = count; | |
281 entries_[0].n_value = string_size; | |
282 } | |
283 | |
284 void StabSection::GetSection(string *section) { | |
285 section->assign(reinterpret_cast<char *>(entries_), | |
286 sizeof(*entries_) * used_); | |
287 } | |
288 | |
289 void StabSection::Clear() { | |
290 used_ = 0; | |
291 size_ = 1; | |
292 entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_); | |
293 } | |
294 | |
295 // A class for building .stabstr sections. | |
296 // | |
297 // A .stabstr section is an array of characters containing a bunch of | |
298 // null-terminated strings. A string is identified by the index of | |
299 // its initial character in the array. The array always starts with a | |
300 // null byte, so that an index of zero refers to the empty string. | |
301 // | |
302 // This implementation also ensures that if two strings are equal, we | |
303 // assign them the same indices; most linkers do this, and some | |
304 // clients may rely upon it. (Note that this is not quite the same as | |
305 // ensuring that a string only appears once in the section; you could | |
306 // share space when one string is a suffix of another, but we don't.) | |
307 class StabstrSection { | |
308 public: | |
309 StabstrSection(): next_byte_(1) { string_indices_[""] = 0; } | |
310 // Ensure STR is present in the string section, and return its index. | |
311 size_t Insert(const string &str); | |
312 // Set SECTION to the contents of a .stabstr section in which the | |
313 // strings passed to Insert appear at the indices we promised. | |
314 void GetSection(string *section); | |
315 // Clear the contents of this StabstrSection, and prepare it to | |
316 // accumulate a new set of strings. | |
317 void Clear(); | |
318 private: | |
319 // Maps from strings to .stabstr indices and back. | |
320 typedef map<string, size_t> StringToIndex; | |
321 typedef map<size_t, const string *> IndexToString; | |
322 | |
323 // A map from strings to the indices we've assigned them. | |
324 StringToIndex string_indices_; | |
325 | |
326 // The next unused byte in the section. The next string we add | |
327 // will get this index. | |
328 size_t next_byte_; | |
329 }; | |
330 | |
331 size_t StabstrSection::Insert(const string &str) { | |
332 StringToIndex::iterator it = string_indices_.find(str); | |
333 size_t index; | |
334 if (it != string_indices_.end()) { | |
335 index = it->second; | |
336 } else { | |
337 // This is the first time we've seen STR; add it to the table. | |
338 string_indices_[str] = next_byte_; | |
339 index = next_byte_; | |
340 next_byte_ += str.size() + 1; | |
341 } | |
342 return index; | |
343 } | |
344 | |
345 void StabstrSection::GetSection(string *section) { | |
346 // First we have to invert the map. | |
347 IndexToString byIndex; | |
348 for (StringToIndex::const_iterator it = string_indices_.begin(); | |
349 it != string_indices_.end(); it++) | |
350 byIndex[it->second] = &it->first; | |
351 // Now we build the .stabstr section. | |
352 section->clear(); | |
353 for (IndexToString::const_iterator it = byIndex.begin(); | |
354 it != byIndex.end(); it++) { | |
355 // Make sure we're actually assigning it the index we claim to be. | |
356 assert(it->first == section->size()); | |
357 *section += *(it->second); | |
358 *section += '\0'; | |
359 } | |
360 } | |
361 | |
362 void StabstrSection::Clear() { | |
363 string_indices_.clear(); | |
364 string_indices_[""] = 0; | |
365 next_byte_ = 1; | |
366 } | |
367 | |
368 // A mock stabs parser handler class that builds .stab and .stabstr | |
369 // sections. | |
370 class StabsSectionsBuilder: public MockStabsHandler { | |
371 public: | |
372 // Construct a handler that will receive data from a MockStabsParser | |
373 // and construct .stab and .stabstr sections. FILENAME should be | |
374 // the name of the mock stabs input file; we use it in error | |
375 // messages. | |
376 StabsSectionsBuilder(const string &filename) | |
377 : filename_(filename), error_count_(0), has_header_(false), | |
378 entry_count_(0) { } | |
379 | |
380 // Overridden virtual member functions. | |
381 bool Entry(enum __stab_debug_code type, char other, short desc, | |
382 unsigned long value, const string &name); | |
383 bool CUBoundary(const string &filename); | |
384 bool Error(const char *format, ...); | |
385 | |
386 // Set SECTION to the contents of a .stab or .stabstr section | |
387 // reflecting the entries that have been passed to us via Entry. | |
388 void GetStab(string *section); | |
389 void GetStabstr(string *section); | |
390 | |
391 private: | |
392 // Finish a compilation unit. If there are any entries accumulated in | |
393 // stab_ and stabstr_, add them as a new compilation unit to | |
394 // finished_cu_stabs_ and finished_cu_stabstr_, and then clear stab_ and | |
395 // stabstr_. | |
396 void FinishCU(); | |
397 | |
398 const string &filename_; // input filename, for error messages | |
399 int error_count_; // number of errors we've seen so far | |
400 | |
401 // The following members accumulate the contents of a single compilation | |
402 // unit, possibly headed by an N_UNDF stab. | |
403 bool has_header_; // true if we have an N_UNDF header | |
404 int entry_count_; // the number of entries we've seen | |
405 StabSection stab_; // 'struct nlist' entries | |
406 StabstrSection stabstr_; // and the strings they love | |
407 | |
408 // Accumulated .stab and .stabstr content for all compilation units. | |
409 string finished_cu_stab_, finished_cu_stabstr_; | |
410 }; | |
411 | |
412 bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other, | |
413 short desc, unsigned long value, | |
414 const string &name) { | |
415 struct nlist *entry = stab_.Append(); | |
416 entry->n_type = type; | |
417 entry->n_other = other; | |
418 entry->n_desc = desc; | |
419 entry->n_value = value; | |
420 entry->n_un.n_strx = stabstr_.Insert(name); | |
421 entry_count_++; | |
422 return true; | |
423 } | |
424 | |
425 bool StabsSectionsBuilder::CUBoundary(const string &filename) { | |
426 FinishCU(); | |
427 // Add a header for the compilation unit. | |
428 assert(!has_header_); | |
429 assert(entry_count_ == 0); | |
430 struct nlist *entry = stab_.Append(); | |
431 entry->n_type = N_UNDF; | |
432 entry->n_other = 0; | |
433 entry->n_desc = 0; // will be set to number of entries | |
434 entry->n_value = 0; // will be set to size of .stabstr data | |
435 entry->n_un.n_strx = stabstr_.Insert(filename); | |
436 has_header_ = true; | |
437 // The N_UNDF header isn't included in the symbol count, so we | |
438 // shouldn't bump entry_count_ here. | |
439 return true; | |
440 } | |
441 | |
442 void StabsSectionsBuilder::FinishCU() { | |
443 if (entry_count_ > 0) { | |
444 // Get the strings first, so we can record their size in the header. | |
445 string stabstr; | |
446 stabstr_.GetSection(&stabstr); | |
447 finished_cu_stabstr_ += stabstr; | |
448 | |
449 // Initialize our header, if we have one, and extract the .stab data. | |
450 if (has_header_) | |
451 stab_.SetHeader(entry_count_, stabstr.size()); | |
452 string stab; | |
453 stab_.GetSection(&stab); | |
454 finished_cu_stab_ += stab; | |
455 } | |
456 | |
457 stab_.Clear(); | |
458 stabstr_.Clear(); | |
459 has_header_ = false; | |
460 entry_count_ = 0; | |
461 } | |
462 | |
463 bool StabsSectionsBuilder::Error(const char *format, ...) { | |
464 va_list args; | |
465 va_start(args, format); | |
466 vfprintf(stderr, format, args); | |
467 va_end(args); | |
468 error_count_++; | |
469 if (error_count_ >= 20) { | |
470 fprintf(stderr, | |
471 "%s: lots of errors; is this really a mock stabs file?\n", | |
472 filename_.c_str()); | |
473 return false; | |
474 } | |
475 return true; | |
476 } | |
477 | |
478 void StabsSectionsBuilder::GetStab(string *section) { | |
479 FinishCU(); | |
480 *section = finished_cu_stab_; | |
481 } | |
482 | |
483 void StabsSectionsBuilder::GetStabstr(string *section) { | |
484 FinishCU(); | |
485 *section = finished_cu_stabstr_; | |
486 } | |
487 | |
488 class MockStabsReaderHandler: public StabsHandler { | |
489 public: | |
490 MOCK_METHOD3(StartCompilationUnit, | |
491 bool(const char *, uint64_t, const char *)); | |
492 MOCK_METHOD1(EndCompilationUnit, bool(uint64_t)); | |
493 MOCK_METHOD2(StartFunction, bool(const std::string &, uint64_t)); | |
494 MOCK_METHOD1(EndFunction, bool(uint64_t)); | |
495 MOCK_METHOD3(Line, bool(uint64_t, const char *, int)); | |
496 void Warning(const char *format, ...) { MockWarning(format); } | |
497 MOCK_METHOD1(MockWarning, void(const char *)); | |
498 }; | |
499 | |
500 // Create a StabsReader to parse the mock stabs data in INPUT_FILE, | |
501 // passing the parsed information to HANDLER. If all goes well, return | |
502 // the result of calling the reader's Process member function. | |
503 // Otherwise, return false. INPUT_FILE should be relative to the top | |
504 // of the source tree. | |
505 static bool ApplyHandlerToMockStabsData(StabsHandler *handler, | |
506 const string &input_file) { | |
507 string full_input_file | |
508 = string(getenv("srcdir") ? getenv("srcdir") : ".") + "/" + input_file; | |
509 | |
510 // Open the input file. | |
511 std::ifstream stream(full_input_file.c_str()); | |
512 if (stream.fail()) { | |
513 fprintf(stderr, "error opening mock stabs input file %s: %s\n", | |
514 full_input_file.c_str(), strerror(errno)); | |
515 return false; | |
516 } | |
517 | |
518 // Parse the mock stabs data, and produce stabs sections to use as | |
519 // test input to the reader. | |
520 StabsSectionsBuilder builder(full_input_file); | |
521 MockStabsParser mock_parser(full_input_file, &stream, &builder); | |
522 if (!mock_parser.Process()) | |
523 return false; | |
524 string stab, stabstr; | |
525 builder.GetStab(&stab); | |
526 builder.GetStabstr(&stabstr); | |
527 | |
528 // Run the parser on the test input, passing whatever we find to HANDLER. | |
529 StabsReader reader( | |
530 reinterpret_cast<const uint8_t *>(stab.data()), stab.size(), | |
531 reinterpret_cast<const uint8_t *>(stabstr.data()), stabstr.size(), | |
532 handler); | |
533 return reader.Process(); | |
534 } | |
535 | |
536 TEST(StabsReader, MockStabsInput) { | |
537 MockStabsReaderHandler mock_handler; | |
538 | |
539 { | |
540 InSequence s; | |
541 | |
542 EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file1.c"), | |
543 0x42, StrEq("builddir1/"))) | |
544 .WillOnce(Return(true)); | |
545 EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0x62)) | |
546 .WillOnce(Return(true)); | |
547 EXPECT_CALL(mock_handler, Line(0xe4, StrEq("file1.c"), 91)) | |
548 .WillOnce(Return(true)); | |
549 EXPECT_CALL(mock_handler, Line(0x164, StrEq("header.h"), 111)) | |
550 .WillOnce(Return(true)); | |
551 EXPECT_CALL(mock_handler, EndFunction(0x112)) | |
552 .WillOnce(Return(true)); | |
553 EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0x112)) | |
554 .WillOnce(Return(true)); | |
555 EXPECT_CALL(mock_handler, Line(0x234, StrEq("header.h"), 131)) | |
556 .WillOnce(Return(true)); | |
557 EXPECT_CALL(mock_handler, Line(0x254, StrEq("file1.c"), 151)) | |
558 .WillOnce(Return(true)); | |
559 EXPECT_CALL(mock_handler, EndFunction(0x152)) | |
560 .WillOnce(Return(true)); | |
561 EXPECT_CALL(mock_handler, EndCompilationUnit(0x152)) | |
562 .WillOnce(Return(true)); | |
563 EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"), | |
564 0x182, NULL)) | |
565 .WillOnce(Return(true)); | |
566 EXPECT_CALL(mock_handler, EndCompilationUnit(0x192)) | |
567 .WillOnce(Return(true)); | |
568 } | |
569 | |
570 ASSERT_TRUE(ApplyHandlerToMockStabsData( | |
571 &mock_handler, | |
572 "common/testdata/stabs_reader_unittest.input1")); | |
573 } | |
574 | |
575 TEST(StabsReader, AbruptCU) { | |
576 MockStabsReaderHandler mock_handler; | |
577 | |
578 { | |
579 InSequence s; | |
580 | |
581 EXPECT_CALL(mock_handler, | |
582 StartCompilationUnit(StrEq("file2-1.c"), 0x12, NULL)) | |
583 .WillOnce(Return(true)); | |
584 EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) | |
585 .WillOnce(Return(true)); | |
586 } | |
587 | |
588 ASSERT_TRUE(ApplyHandlerToMockStabsData( | |
589 &mock_handler, | |
590 "common/testdata/stabs_reader_unittest.input2")); | |
591 } | |
592 | |
593 TEST(StabsReader, AbruptFunction) { | |
594 MockStabsReaderHandler mock_handler; | |
595 | |
596 { | |
597 InSequence s; | |
598 | |
599 EXPECT_CALL(mock_handler, | |
600 StartCompilationUnit(StrEq("file3-1.c"), 0x12, NULL)) | |
601 .WillOnce(Return(true)); | |
602 EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0x22)) | |
603 .WillOnce(Return(true)); | |
604 EXPECT_CALL(mock_handler, EndFunction(NULL)) | |
605 .WillOnce(Return(true)); | |
606 EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) | |
607 .WillOnce(Return(true)); | |
608 } | |
609 | |
610 ASSERT_TRUE(ApplyHandlerToMockStabsData( | |
611 &mock_handler, | |
612 "common/testdata/stabs_reader_unittest.input3")); | |
613 } | |
614 | |
615 TEST(StabsReader, NoCU) { | |
616 MockStabsReaderHandler mock_handler; | |
617 | |
618 EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _)) | |
619 .Times(0); | |
620 EXPECT_CALL(mock_handler, StartFunction(_, _)) | |
621 .Times(0); | |
622 | |
623 ASSERT_TRUE(ApplyHandlerToMockStabsData( | |
624 &mock_handler, | |
625 "common/testdata/stabs_reader_unittest.input4")); | |
626 | |
627 } | |
628 | |
629 TEST(StabsReader, NoCUEnd) { | |
630 MockStabsReaderHandler mock_handler; | |
631 | |
632 { | |
633 InSequence s; | |
634 | |
635 EXPECT_CALL(mock_handler, | |
636 StartCompilationUnit(StrEq("file5-1.c"), 0x12, NULL)) | |
637 .WillOnce(Return(true)); | |
638 EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) | |
639 .WillOnce(Return(true)); | |
640 EXPECT_CALL(mock_handler, | |
641 StartCompilationUnit(StrEq("file5-2.c"), 0x22, NULL)) | |
642 .WillOnce(Return(true)); | |
643 EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) | |
644 .WillOnce(Return(true)); | |
645 } | |
646 | |
647 ASSERT_TRUE(ApplyHandlerToMockStabsData( | |
648 &mock_handler, | |
649 "common/testdata/stabs_reader_unittest.input5")); | |
650 | |
651 } | |
652 | |
653 TEST(StabsReader, MultipleCUs) { | |
654 MockStabsReaderHandler mock_handler; | |
655 | |
656 { | |
657 InSequence s; | |
658 EXPECT_CALL(mock_handler, | |
659 StartCompilationUnit(StrEq("antimony"), 0x12, NULL)) | |
660 .WillOnce(Return(true)); | |
661 EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x22)) | |
662 .WillOnce(Return(true)); | |
663 EXPECT_CALL(mock_handler, EndFunction(0x32)) | |
664 .WillOnce(Return(true)); | |
665 EXPECT_CALL(mock_handler, EndCompilationUnit(0x32)) | |
666 .WillOnce(Return(true)); | |
667 EXPECT_CALL(mock_handler, | |
668 StartCompilationUnit(StrEq("aluminum"), 0x42, NULL)) | |
669 .WillOnce(Return(true)); | |
670 EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0x52)) | |
671 .WillOnce(Return(true)); | |
672 EXPECT_CALL(mock_handler, EndFunction(0x62)) | |
673 .WillOnce(Return(true)); | |
674 EXPECT_CALL(mock_handler, EndCompilationUnit(0x62)) | |
675 .WillOnce(Return(true)); | |
676 } | |
677 | |
678 ASSERT_TRUE(ApplyHandlerToMockStabsData( | |
679 &mock_handler, | |
680 "common/testdata/stabs_reader_unittest.input6")); | |
681 } | |
682 | |
683 // name duplication | |
684 | |
685 } // anonymous namespace | |
OLD | NEW |