Coverage for /Syzygy/refinery/analyzers/stack_analysis_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
0.0%00151.C++test

Line-by-line coverage:

   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 <Windows.h>  // NOLINT
  16    :  #include <DbgHelp.h>
  17    :  
  18    :  #include <string>
  19    :  #include <vector>
  20    :  
  21    :  #include "base/debug/alias.h"
  22    :  #include "base/files/file_path.h"
  23    :  #include "base/memory/scoped_ptr.h"
  24    :  #include "base/threading/platform_thread.h"
  25    :  #include "base/win/scoped_com_initializer.h"
  26    :  #include "gtest/gtest.h"
  27    :  #include "syzygy/common/com_utils.h"
  28    :  #include "syzygy/common/unittest_util.h"
  29    :  #include "syzygy/minidump/minidump.h"
  30    :  #include "syzygy/pe/unittest_util.h"
  31    :  #include "syzygy/refinery/unittest_util.h"
  32    :  #include "syzygy/refinery/analyzers/analysis_runner.h"
  33    :  #include "syzygy/refinery/analyzers/exception_analyzer.h"
  34    :  #include "syzygy/refinery/analyzers/memory_analyzer.h"
  35    :  #include "syzygy/refinery/analyzers/module_analyzer.h"
  36    :  #include "syzygy/refinery/analyzers/stack_analyzer.h"
  37    :  #include "syzygy/refinery/analyzers/stack_frame_analyzer.h"
  38    :  #include "syzygy/refinery/analyzers/thread_analyzer.h"
  39    :  #include "syzygy/refinery/process_state/process_state.h"
  40    :  #include "syzygy/refinery/process_state/process_state_util.h"
  41    :  #include "syzygy/refinery/process_state/refinery.pb.h"
  42    :  #include "syzygy/refinery/symbols/dia_symbol_provider.h"
  43    :  
  44  m :  namespace refinery {
  45    :  
  46  m :  namespace {
  47    :  
  48  m :  struct SimpleUDT {
  49  m :    int one;
  50  m :    const char two;
  51  m :  };
  52    :  
  53  m :  __declspec(noinline) DWORD GetEip() {
  54  m :    return reinterpret_cast<DWORD>(_ReturnAddress());
  55  m :  }
  56    :  
  57  m :  }  // namespace
  58    :  
  59  m :  class StackAndFrameAnalyzersTest : public testing::Test {
  60  m :   protected:
  61  m :    void SetUp() override {
  62    :      // Override NT symbol path.
  63  m :      ASSERT_TRUE(scoped_symbol_path_.Setup());
  64    :  
  65  m :      symbol_provider_ = new SymbolProvider();
  66    :  
  67  m :      expected_esp_ = 0U;
  68  m :      eip_lowerbound_ = 0U;
  69  m :      eip_upperbound_ = 0U;
  70    :  
  71  m :      expected_param_address_ = 0ULL;
  72  m :      expected_udt_address_ = 0ULL;
  73  m :      expected_udt_ptr_address_ = 0ULL;
  74  m :    }
  75    :  
  76  m :    base::FilePath minidump_path() { return scoped_minidump_.minidump_path(); }
  77  m :    uint32 expected_esp() { return expected_esp_; }
  78  m :    uint32 eip_lowerbound() { return eip_lowerbound_; }
  79  m :    uint32 eip_upperbound() { return eip_upperbound_; }
  80  m :    Address expected_param_address() { return expected_param_address_; }
  81  m :    Address expected_udt_address() { return expected_udt_address_; }
  82  m :    Address expected_udt_ptr_address() { return expected_udt_ptr_address_; }
  83    :  
  84  m :    bool SetupStackFrameAndGenerateMinidump(int dummy_param) {
  85  m :      bool success = true;
  86    :  
  87    :      // Create some local variables to validate analysis.
  88  m :      SimpleUDT udt_local = {42, 'a'};
  89  m :      base::debug::Alias(&udt_local);
  90  m :      SimpleUDT* udt_ptr_local = &udt_local;
  91  m :      base::debug::Alias(&udt_ptr_local);
  92    :  
  93    :      // Copy esp to expected_esp_. Note: esp must not be changed prior to calling
  94    :      // GenerateMinidump.
  95  m :      __asm {
  96  m :        mov ebx, this
  97  m :        mov [ebx].expected_esp_, esp
  98  m :      }
  99    :  
 100  m :      eip_lowerbound_ = GetEip();
 101    :  
 102    :      // Note: GenerateMinidump takes one parameter. This means when the frame
 103    :      // is walked, its top should equal the captured esp less the size of that
 104    :      // argument.
 105  m :      expected_esp_ -= sizeof(testing::ScopedMinidump::kMinidumpWithStacks);
 106  m :      success = scoped_minidump_.GenerateMinidump(
 107  m :          testing::ScopedMinidump::kMinidumpWithStacks);
 108    :  
 109  m :      eip_upperbound_ = GetEip();
 110    :  
 111  m :      expected_param_address_ = reinterpret_cast<Address>(&dummy_param);
 112  m :      expected_udt_address_ = reinterpret_cast<Address>(&udt_local);
 113  m :      expected_udt_ptr_address_ = reinterpret_cast<Address>(&udt_ptr_local);
 114    :  
 115  m :      return success;
 116  m :    }
 117    :  
 118  m :    bool AnalyzeMinidump(ProcessState* process_state) {
 119  m :      minidump::Minidump minidump;
 120  m :      if (!minidump.Open(minidump_path()))
 121  m :        return false;
 122    :  
 123  m :      scoped_refptr<DiaSymbolProvider> dia_symbol_provider(
 124  m :          new DiaSymbolProvider());
 125    :  
 126  m :      AnalysisRunner runner;
 127  m :      scoped_ptr<Analyzer> analyzer(new refinery::MemoryAnalyzer());
 128  m :      runner.AddAnalyzer(analyzer.Pass());
 129  m :      analyzer.reset(new refinery::ThreadAnalyzer());
 130  m :      runner.AddAnalyzer(analyzer.Pass());
 131  m :      analyzer.reset(new refinery::ExceptionAnalyzer());
 132  m :      runner.AddAnalyzer(analyzer.Pass());
 133  m :      analyzer.reset(new refinery::ModuleAnalyzer());
 134  m :      runner.AddAnalyzer(analyzer.Pass());
 135  m :      analyzer.reset(new refinery::StackAnalyzer(dia_symbol_provider));
 136  m :      runner.AddAnalyzer(analyzer.Pass());
 137  m :      analyzer.reset(new refinery::StackFrameAnalyzer(dia_symbol_provider,
 138  m :                                                      symbol_provider_));
 139  m :      runner.AddAnalyzer(analyzer.Pass());
 140    :  
 141  m :      return runner.Analyze(minidump, process_state) ==
 142  m :             Analyzer::ANALYSIS_COMPLETE;
 143  m :    }
 144    :  
 145  m :    void ValidateTypedBlock(ProcessState* process_state,
 146  m :                            Address expected_address,
 147  m :                            Size expected_size,
 148  m :                            ModuleId expected_module_id,
 149  m :                            const std::string& expected_variable_name,
 150  m :                            const base::string16& expected_type_name) {
 151  m :      TypedBlockRecordPtr typedblock_record;
 152    :      // Note: using FindSingleRecord as there should be no typed block overlap in
 153    :      // the context of this test.
 154  m :      ASSERT_TRUE(
 155  m :          process_state->FindSingleRecord(expected_address, &typedblock_record));
 156    :  
 157  m :      ASSERT_EQ(expected_address, typedblock_record->range().start());
 158  m :      ASSERT_EQ(expected_size, typedblock_record->range().size());
 159    :  
 160  m :      const TypedBlock& typedblock = typedblock_record->data();
 161  m :      ASSERT_EQ(expected_module_id, typedblock.module_id());
 162    :  
 163    :      // Validate the recovered type id corresponds to the expected name.
 164  m :      ModuleLayerAccessor accessor(process_state);
 165  m :      pe::PEFile::Signature signature;
 166  m :      ASSERT_TRUE(accessor.GetModuleSignature(expected_module_id, &signature));
 167    :  
 168  m :      scoped_refptr<TypeRepository> type_repository;
 169  m :      ASSERT_TRUE(symbol_provider_->FindOrCreateTypeRepository(signature,
 170  m :                                                               &type_repository));
 171    :  
 172  m :      TypePtr recovered_type = type_repository->GetType(typedblock.type_id());
 173  m :      ASSERT_NE(nullptr, recovered_type);
 174  m :      ASSERT_EQ(expected_type_name, recovered_type->name());
 175    :  
 176  m :      ASSERT_EQ(expected_variable_name, typedblock.data_name());
 177  m :    }
 178    :  
 179  m :   private:
 180  m :    testing::ScopedMinidump scoped_minidump_;
 181    :  
 182  m :    scoped_refptr<SymbolProvider> symbol_provider_;
 183    :  
 184    :    // For stack frame validation.
 185  m :    uint32 expected_esp_;
 186  m :    uint32 eip_lowerbound_;
 187  m :    uint32 eip_upperbound_;
 188    :  
 189    :    // Typed block validation.
 190  m :    Address expected_param_address_;
 191  m :    Address expected_udt_address_;
 192  m :    Address expected_udt_ptr_address_;
 193    :  
 194  m :    testing::ScopedSymbolPath scoped_symbol_path_;
 195  m :  };
 196    :  
 197    :  // This test fails under coverage instrumentation which is probably not friendly
 198    :  // to stackwalking.
 199    :  #ifdef _COVERAGE_BUILD
 200  m :  TEST_F(StackAndFrameAnalyzersTest, DISABLED_BasicTest) {
 201    :  #else
 202  m :  TEST_F(StackAndFrameAnalyzersTest, BasicTest) {
 203    :  #endif
 204  m :    base::win::ScopedCOMInitializer com_initializer;
 205    :  
 206    :    // Note: intentionally declared before determining expected_frame_base.
 207  m :    int dummy_argument = 22;
 208    :  
 209    :    // Generate the minidump, then analyze it.
 210    :    // Note: the expected frame base for SetupStackFrameAndGenerateMinidump should
 211    :    // be sizeof(void*) + sizeof(int) off of the current frame's top of stack
 212    :    // immediately prior to the call (accounting for callee argument and return
 213    :    // address).
 214  m :    uint32 expected_frame_base = 0U;
 215  m :    __asm {
 216  m :      mov expected_frame_base, esp
 217  m :    }
 218  m :    expected_frame_base -= (sizeof(void*) + sizeof(int));
 219    :  
 220  m :    ASSERT_TRUE(SetupStackFrameAndGenerateMinidump(dummy_argument));
 221    :  
 222  m :    ProcessState process_state;
 223  m :    ASSERT_TRUE(AnalyzeMinidump(&process_state));
 224    :  
 225    :    // Ensure the test's thread was successfully walked.
 226  m :    StackRecordPtr stack;
 227  m :    DWORD thread_id = ::GetCurrentThreadId();
 228  m :    ASSERT_TRUE(
 229  m :        process_state.FindStackRecord(static_cast<size_t>(thread_id), &stack));
 230  m :    ASSERT_TRUE(stack->data().stack_walk_success());
 231    :  
 232    :    // Validate SetupStackFrameAndGenerateMinidump's frame.
 233  m :    StackFrameRecordPtr frame_record;
 234    :    // Note: using FindSingleRecord as there should be no frame record overlap
 235    :    // in the context of this test.
 236  m :    ASSERT_TRUE(process_state.FindSingleRecord(
 237  m :        static_cast<Address>(expected_esp()), &frame_record));
 238    :  
 239  m :    ASSERT_EQ(expected_esp(), frame_record->range().start());
 240  m :    ASSERT_EQ(expected_frame_base - expected_esp(), frame_record->range().size());
 241    :  
 242  m :    const StackFrame& frame = frame_record->data();
 243  m :    uint32_t recovered_eip = frame.register_info().eip();
 244  m :    ASSERT_LT(eip_lowerbound(), recovered_eip);
 245  m :    ASSERT_GT(eip_upperbound(), recovered_eip);
 246    :  
 247    :    // Sanity and tightness check.
 248  m :    ASSERT_GT(eip_upperbound(), eip_lowerbound());
 249  m :    ASSERT_LT(eip_upperbound() - eip_lowerbound(), 100);
 250    :  
 251    :    // TODO(manzagop): validate frame_size_bytes. It should be sizeof(void*)
 252    :    // smaller than expected_frame_base - expected_esp(), to account for ebp
 253    :    // and since the function called into has no parameters.
 254    :  
 255    :    // TODO(manzagop): validate locals_base. It should be sizeof(void*) off of
 256    :    // the frame base, to account for ebp.
 257    :  
 258    :    // Validate typed block layer for SetupStackFrameAndGenerateMinidump.
 259  m :    ModuleLayerAccessor accessor(&process_state);
 260  m :    ModuleId expected_module_id = accessor.GetModuleId(recovered_eip);
 261  m :    ASSERT_NE(kNoModuleId, expected_module_id);
 262    :  
 263    :    // - Validate some locals.
 264  m :    ASSERT_NO_FATAL_FAILURE(
 265  m :        ValidateTypedBlock(&process_state, expected_udt_address(),
 266  m :                           sizeof(SimpleUDT), expected_module_id, "udt_local",
 267  m :                           L"refinery::`anonymous-namespace'::SimpleUDT"));
 268  m :    ASSERT_NO_FATAL_FAILURE(ValidateTypedBlock(
 269  m :        &process_state, expected_udt_ptr_address(), sizeof(SimpleUDT*),
 270  m :        expected_module_id, "udt_ptr_local",
 271  m :        L"refinery::`anonymous-namespace'::SimpleUDT*"));
 272    :    // - Validate a parameter.
 273  m :    ASSERT_NO_FATAL_FAILURE(
 274  m :        ValidateTypedBlock(&process_state, expected_param_address(), sizeof(int),
 275  m :                           expected_module_id, "dummy_param", L"int32_t"));
 276  m :  }
 277    :  
 278  m :  }  // namespace refinery

Coverage information generated Thu Jan 14 17:40:38 2016.