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

Coverage information generated Fri Jul 29 11:00:21 2016.