Home | History | Annotate | Download | only in processor
      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 // fast_source_line_resolver_unittest.cc: Unit tests for FastSourceLineResolver.
     31 // Two different approaches for testing fast source line resolver:
     32 // First, use the same unit test data for basic source line resolver.
     33 // Second, read data from symbol files, load them as basic modules, and then
     34 // serialize them and load the serialized data as fast modules.  Then compare
     35 // modules to assure the fast module contains exactly the same data as
     36 // basic module.
     37 //
     38 // Author: Siyang Xie (lambxsy (at) google.com)
     39 
     40 #include <assert.h>
     41 #include <stdio.h>
     42 
     43 #include <sstream>
     44 #include <string>
     45 
     46 #include "breakpad_googletest_includes.h"
     47 #include "common/using_std_string.h"
     48 #include "google_breakpad/processor/code_module.h"
     49 #include "google_breakpad/processor/stack_frame.h"
     50 #include "google_breakpad/processor/memory_region.h"
     51 #include "processor/logging.h"
     52 #include "processor/module_serializer.h"
     53 #include "processor/module_comparer.h"
     54 
     55 namespace {
     56 
     57 using google_breakpad::SourceLineResolverBase;
     58 using google_breakpad::BasicSourceLineResolver;
     59 using google_breakpad::FastSourceLineResolver;
     60 using google_breakpad::ModuleSerializer;
     61 using google_breakpad::ModuleComparer;
     62 using google_breakpad::CFIFrameInfo;
     63 using google_breakpad::CodeModule;
     64 using google_breakpad::MemoryRegion;
     65 using google_breakpad::StackFrame;
     66 using google_breakpad::WindowsFrameInfo;
     67 using google_breakpad::linked_ptr;
     68 using google_breakpad::scoped_ptr;
     69 
     70 class TestCodeModule : public CodeModule {
     71  public:
     72   explicit TestCodeModule(string code_file) : code_file_(code_file) {}
     73   virtual ~TestCodeModule() {}
     74 
     75   virtual uint64_t base_address() const { return 0; }
     76   virtual uint64_t size() const { return 0xb000; }
     77   virtual string code_file() const { return code_file_; }
     78   virtual string code_identifier() const { return ""; }
     79   virtual string debug_file() const { return ""; }
     80   virtual string debug_identifier() const { return ""; }
     81   virtual string version() const { return ""; }
     82   virtual const CodeModule* Copy() const {
     83     return new TestCodeModule(code_file_);
     84   }
     85 
     86  private:
     87   string code_file_;
     88 };
     89 
     90 // A mock memory region object, for use by the STACK CFI tests.
     91 class MockMemoryRegion: public MemoryRegion {
     92   uint64_t GetBase() const { return 0x10000; }
     93   uint32_t GetSize() const { return 0x01000; }
     94   bool GetMemoryAtAddress(uint64_t address, uint8_t *value) const {
     95     *value = address & 0xff;
     96     return true;
     97   }
     98   bool GetMemoryAtAddress(uint64_t address, uint16_t *value) const {
     99     *value = address & 0xffff;
    100     return true;
    101   }
    102   bool GetMemoryAtAddress(uint64_t address, uint32_t *value) const {
    103     switch (address) {
    104       case 0x10008: *value = 0x98ecadc3; break;  // saved %ebx
    105       case 0x1000c: *value = 0x878f7524; break;  // saved %esi
    106       case 0x10010: *value = 0x6312f9a5; break;  // saved %edi
    107       case 0x10014: *value = 0x10038;    break;  // caller's %ebp
    108       case 0x10018: *value = 0xf6438648; break;  // return address
    109       default: *value = 0xdeadbeef;      break;  // junk
    110     }
    111     return true;
    112   }
    113   bool GetMemoryAtAddress(uint64_t address, uint64_t *value) const {
    114     *value = address;
    115     return true;
    116   }
    117   void Print() const {
    118     assert(false);
    119   }
    120 };
    121 
    122 // Verify that, for every association in ACTUAL, EXPECTED has the same
    123 // association. (That is, ACTUAL's associations should be a subset of
    124 // EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
    125 // ".cfa".
    126 static bool VerifyRegisters(
    127     const char *file, int line,
    128     const CFIFrameInfo::RegisterValueMap<uint32_t> &expected,
    129     const CFIFrameInfo::RegisterValueMap<uint32_t> &actual) {
    130   CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator a;
    131   a = actual.find(".cfa");
    132   if (a == actual.end())
    133     return false;
    134   a = actual.find(".ra");
    135   if (a == actual.end())
    136     return false;
    137   for (a = actual.begin(); a != actual.end(); a++) {
    138     CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator e =
    139       expected.find(a->first);
    140     if (e == expected.end()) {
    141       fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
    142               file, line, a->first.c_str(), a->second);
    143       return false;
    144     }
    145     if (e->second != a->second) {
    146       fprintf(stderr,
    147               "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
    148               file, line, a->first.c_str(), a->second, e->second);
    149       return false;
    150     }
    151     // Don't complain if this doesn't recover all registers. Although
    152     // the DWARF spec says that unmentioned registers are undefined,
    153     // GCC uses omission to mean that they are unchanged.
    154   }
    155   return true;
    156 }
    157 
    158 static bool VerifyEmpty(const StackFrame &frame) {
    159   if (frame.function_name.empty() &&
    160       frame.source_file_name.empty() &&
    161       frame.source_line == 0)
    162     return true;
    163   return false;
    164 }
    165 
    166 static void ClearSourceLineInfo(StackFrame *frame) {
    167   frame->function_name.clear();
    168   frame->module = NULL;
    169   frame->source_file_name.clear();
    170   frame->source_line = 0;
    171 }
    172 
    173 class TestFastSourceLineResolver : public ::testing::Test {
    174  public:
    175   void SetUp() {
    176     testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
    177                          "/src/processor/testdata";
    178   }
    179 
    180   string symbol_file(int file_index) {
    181     std::stringstream ss;
    182     ss << testdata_dir << "/module" << file_index << ".out";
    183     return ss.str();
    184   }
    185 
    186   ModuleSerializer serializer;
    187   BasicSourceLineResolver basic_resolver;
    188   FastSourceLineResolver fast_resolver;
    189   ModuleComparer module_comparer;
    190 
    191   string testdata_dir;
    192 };
    193 
    194 // Test adapted from basic_source_line_resolver_unittest.
    195 TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
    196   TestCodeModule module1("module1");
    197   ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
    198   ASSERT_TRUE(basic_resolver.HasModule(&module1));
    199   // Convert module1 to fast_module:
    200   ASSERT_TRUE(serializer.ConvertOneModule(
    201       module1.code_file(), &basic_resolver, &fast_resolver));
    202   ASSERT_TRUE(fast_resolver.HasModule(&module1));
    203 
    204   TestCodeModule module2("module2");
    205   ASSERT_TRUE(basic_resolver.LoadModule(&module2, symbol_file(2)));
    206   ASSERT_TRUE(basic_resolver.HasModule(&module2));
    207   // Convert module2 to fast_module:
    208   ASSERT_TRUE(serializer.ConvertOneModule(
    209       module2.code_file(), &basic_resolver, &fast_resolver));
    210   ASSERT_TRUE(fast_resolver.HasModule(&module2));
    211 
    212   StackFrame frame;
    213   scoped_ptr<WindowsFrameInfo> windows_frame_info;
    214   scoped_ptr<CFIFrameInfo> cfi_frame_info;
    215   frame.instruction = 0x1000;
    216   frame.module = NULL;
    217   fast_resolver.FillSourceLineInfo(&frame);
    218   ASSERT_FALSE(frame.module);
    219   ASSERT_TRUE(frame.function_name.empty());
    220   ASSERT_EQ(frame.function_base, 0U);
    221   ASSERT_TRUE(frame.source_file_name.empty());
    222   ASSERT_EQ(frame.source_line, 0);
    223   ASSERT_EQ(frame.source_line_base, 0U);
    224 
    225   frame.module = &module1;
    226   fast_resolver.FillSourceLineInfo(&frame);
    227   ASSERT_EQ(frame.function_name, "Function1_1");
    228   ASSERT_TRUE(frame.module);
    229   ASSERT_EQ(frame.module->code_file(), "module1");
    230   ASSERT_EQ(frame.function_base, 0x1000U);
    231   ASSERT_EQ(frame.source_file_name, "file1_1.cc");
    232   ASSERT_EQ(frame.source_line, 44);
    233   ASSERT_EQ(frame.source_line_base, 0x1000U);
    234   windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
    235   ASSERT_TRUE(windows_frame_info.get());
    236   ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
    237   ASSERT_EQ(windows_frame_info->program_string,
    238             "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
    239 
    240   ClearSourceLineInfo(&frame);
    241   frame.instruction = 0x800;
    242   frame.module = &module1;
    243   fast_resolver.FillSourceLineInfo(&frame);
    244   ASSERT_TRUE(VerifyEmpty(frame));
    245   windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
    246   ASSERT_FALSE(windows_frame_info.get());
    247 
    248   frame.instruction = 0x1280;
    249   fast_resolver.FillSourceLineInfo(&frame);
    250   ASSERT_EQ(frame.function_name, "Function1_3");
    251   ASSERT_TRUE(frame.source_file_name.empty());
    252   ASSERT_EQ(frame.source_line, 0);
    253   windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
    254   ASSERT_TRUE(windows_frame_info.get());
    255   ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN);
    256   ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
    257   ASSERT_TRUE(windows_frame_info->program_string.empty());
    258 
    259   frame.instruction = 0x1380;
    260   fast_resolver.FillSourceLineInfo(&frame);
    261   ASSERT_EQ(frame.function_name, "Function1_4");
    262   ASSERT_TRUE(frame.source_file_name.empty());
    263   ASSERT_EQ(frame.source_line, 0);
    264   windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
    265   ASSERT_TRUE(windows_frame_info.get());
    266   ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
    267   ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
    268   ASSERT_FALSE(windows_frame_info->program_string.empty());
    269 
    270   frame.instruction = 0x2000;
    271   windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
    272   ASSERT_FALSE(windows_frame_info.get());
    273 
    274   // module1 has STACK CFI records covering 3d40..3def;
    275   // module2 has STACK CFI records covering 3df0..3e9f;
    276   // check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
    277   frame.instruction = 0x3d3f;
    278   frame.module = &module1;
    279   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    280   ASSERT_FALSE(cfi_frame_info.get());
    281 
    282   frame.instruction = 0x3e9f;
    283   frame.module = &module1;
    284   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    285   ASSERT_FALSE(cfi_frame_info.get());
    286 
    287   CFIFrameInfo::RegisterValueMap<uint32_t> current_registers;
    288   CFIFrameInfo::RegisterValueMap<uint32_t> caller_registers;
    289   CFIFrameInfo::RegisterValueMap<uint32_t> expected_caller_registers;
    290   MockMemoryRegion memory;
    291 
    292   // Regardless of which instruction evaluation takes place at, it
    293   // should produce the same values for the caller's registers.
    294   expected_caller_registers[".cfa"] = 0x1001c;
    295   expected_caller_registers[".ra"]  = 0xf6438648;
    296   expected_caller_registers["$ebp"] = 0x10038;
    297   expected_caller_registers["$ebx"] = 0x98ecadc3;
    298   expected_caller_registers["$esi"] = 0x878f7524;
    299   expected_caller_registers["$edi"] = 0x6312f9a5;
    300 
    301   frame.instruction = 0x3d40;
    302   frame.module = &module1;
    303   current_registers.clear();
    304   current_registers["$esp"] = 0x10018;
    305   current_registers["$ebp"] = 0x10038;
    306   current_registers["$ebx"] = 0x98ecadc3;
    307   current_registers["$esi"] = 0x878f7524;
    308   current_registers["$edi"] = 0x6312f9a5;
    309   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    310   ASSERT_TRUE(cfi_frame_info.get());
    311   ASSERT_TRUE(cfi_frame_info.get()
    312               ->FindCallerRegs<uint32_t>(current_registers, memory,
    313                                           &caller_registers));
    314   ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
    315                               expected_caller_registers, caller_registers));
    316 
    317   frame.instruction = 0x3d41;
    318   current_registers["$esp"] = 0x10014;
    319   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    320   ASSERT_TRUE(cfi_frame_info.get());
    321   ASSERT_TRUE(cfi_frame_info.get()
    322               ->FindCallerRegs<uint32_t>(current_registers, memory,
    323                                           &caller_registers));
    324   ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
    325                               expected_caller_registers, caller_registers));
    326 
    327   frame.instruction = 0x3d43;
    328   current_registers["$ebp"] = 0x10014;
    329   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    330   ASSERT_TRUE(cfi_frame_info.get());
    331   ASSERT_TRUE(cfi_frame_info.get()
    332               ->FindCallerRegs<uint32_t>(current_registers, memory,
    333                                           &caller_registers));
    334   VerifyRegisters(__FILE__, __LINE__,
    335                   expected_caller_registers, caller_registers);
    336 
    337   frame.instruction = 0x3d54;
    338   current_registers["$ebx"] = 0x6864f054U;
    339   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    340   ASSERT_TRUE(cfi_frame_info.get());
    341   ASSERT_TRUE(cfi_frame_info.get()
    342               ->FindCallerRegs<uint32_t>(current_registers, memory,
    343                                           &caller_registers));
    344   VerifyRegisters(__FILE__, __LINE__,
    345                   expected_caller_registers, caller_registers);
    346 
    347   frame.instruction = 0x3d5a;
    348   current_registers["$esi"] = 0x6285f79aU;
    349   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    350   ASSERT_TRUE(cfi_frame_info.get());
    351   ASSERT_TRUE(cfi_frame_info.get()
    352               ->FindCallerRegs<uint32_t>(current_registers, memory,
    353                                           &caller_registers));
    354   VerifyRegisters(__FILE__, __LINE__,
    355                   expected_caller_registers, caller_registers);
    356 
    357   frame.instruction = 0x3d84;
    358   current_registers["$edi"] = 0x64061449U;
    359   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
    360   ASSERT_TRUE(cfi_frame_info.get());
    361   ASSERT_TRUE(cfi_frame_info.get()
    362               ->FindCallerRegs<uint32_t>(current_registers, memory,
    363                                           &caller_registers));
    364   VerifyRegisters(__FILE__, __LINE__,
    365                   expected_caller_registers, caller_registers);
    366 
    367   frame.instruction = 0x2900;
    368   frame.module = &module1;
    369   fast_resolver.FillSourceLineInfo(&frame);
    370   ASSERT_EQ(frame.function_name, string("PublicSymbol"));
    371 
    372   frame.instruction = 0x4000;
    373   frame.module = &module1;
    374   fast_resolver.FillSourceLineInfo(&frame);
    375   ASSERT_EQ(frame.function_name, string("LargeFunction"));
    376 
    377   frame.instruction = 0x2181;
    378   frame.module = &module2;
    379   fast_resolver.FillSourceLineInfo(&frame);
    380   ASSERT_EQ(frame.function_name, "Function2_2");
    381   ASSERT_EQ(frame.function_base, 0x2170U);
    382   ASSERT_TRUE(frame.module);
    383   ASSERT_EQ(frame.module->code_file(), "module2");
    384   ASSERT_EQ(frame.source_file_name, "file2_2.cc");
    385   ASSERT_EQ(frame.source_line, 21);
    386   ASSERT_EQ(frame.source_line_base, 0x2180U);
    387   windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
    388   ASSERT_TRUE(windows_frame_info.get());
    389   ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
    390   ASSERT_EQ(windows_frame_info->prolog_size, 1U);
    391 
    392   frame.instruction = 0x216f;
    393   fast_resolver.FillSourceLineInfo(&frame);
    394   ASSERT_EQ(frame.function_name, "Public2_1");
    395 
    396   ClearSourceLineInfo(&frame);
    397   frame.instruction = 0x219f;
    398   frame.module = &module2;
    399   fast_resolver.FillSourceLineInfo(&frame);
    400   ASSERT_TRUE(frame.function_name.empty());
    401 
    402   frame.instruction = 0x21a0;
    403   frame.module = &module2;
    404   fast_resolver.FillSourceLineInfo(&frame);
    405   ASSERT_EQ(frame.function_name, "Public2_2");
    406 }
    407 
    408 TEST_F(TestFastSourceLineResolver, TestInvalidLoads) {
    409   TestCodeModule module3("module3");
    410   ASSERT_TRUE(basic_resolver.LoadModule(&module3,
    411                                         testdata_dir + "/module3_bad.out"));
    412   ASSERT_TRUE(basic_resolver.HasModule(&module3));
    413   ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module3));
    414   // Convert module3 to fast_module:
    415   ASSERT_TRUE(serializer.ConvertOneModule(module3.code_file(),
    416                                           &basic_resolver,
    417                                           &fast_resolver));
    418   ASSERT_TRUE(fast_resolver.HasModule(&module3));
    419   ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&module3));
    420 
    421   TestCodeModule module4("module4");
    422   ASSERT_TRUE(basic_resolver.LoadModule(&module4,
    423                                         testdata_dir + "/module4_bad.out"));
    424   ASSERT_TRUE(basic_resolver.HasModule(&module4));
    425   ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module4));
    426   // Convert module4 to fast_module:
    427   ASSERT_TRUE(serializer.ConvertOneModule(module4.code_file(),
    428                                           &basic_resolver,
    429                                           &fast_resolver));
    430   ASSERT_TRUE(fast_resolver.HasModule(&module4));
    431   ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&module4));
    432 
    433   TestCodeModule module5("module5");
    434   ASSERT_FALSE(fast_resolver.LoadModule(&module5,
    435                                         testdata_dir + "/invalid-filename"));
    436   ASSERT_FALSE(fast_resolver.HasModule(&module5));
    437 
    438   TestCodeModule invalidmodule("invalid-module");
    439   ASSERT_FALSE(fast_resolver.HasModule(&invalidmodule));
    440 }
    441 
    442 TEST_F(TestFastSourceLineResolver, TestUnload) {
    443   TestCodeModule module1("module1");
    444   ASSERT_FALSE(basic_resolver.HasModule(&module1));
    445 
    446   ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
    447   ASSERT_TRUE(basic_resolver.HasModule(&module1));
    448   // Convert module1 to fast_module.
    449   ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
    450                                           &basic_resolver,
    451                                           &fast_resolver));
    452   ASSERT_TRUE(fast_resolver.HasModule(&module1));
    453   basic_resolver.UnloadModule(&module1);
    454   fast_resolver.UnloadModule(&module1);
    455   ASSERT_FALSE(fast_resolver.HasModule(&module1));
    456 
    457   ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
    458   ASSERT_TRUE(basic_resolver.HasModule(&module1));
    459   // Convert module1 to fast_module.
    460   ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
    461                                           &basic_resolver,
    462                                           &fast_resolver));
    463   ASSERT_TRUE(fast_resolver.HasModule(&module1));
    464 }
    465 
    466 TEST_F(TestFastSourceLineResolver, CompareModule) {
    467   char *symbol_data;
    468   size_t symbol_data_size;
    469   string symbol_data_string;
    470   string filename;
    471 
    472   for (int module_index = 0; module_index < 3; ++module_index) {
    473     std::stringstream ss;
    474     ss << testdata_dir << "/module" << module_index << ".out";
    475     filename = ss.str();
    476     ASSERT_TRUE(SourceLineResolverBase::ReadSymbolFile(
    477         symbol_file(module_index), &symbol_data, &symbol_data_size));
    478     symbol_data_string.assign(symbol_data, symbol_data_size);
    479     delete [] symbol_data;
    480     ASSERT_TRUE(module_comparer.Compare(symbol_data_string));
    481   }
    482 }
    483 
    484 }  // namespace
    485 
    486 int main(int argc, char *argv[]) {
    487   ::testing::InitGoogleTest(&argc, argv);
    488   return RUN_ALL_TESTS();
    489 }
    490