| Index: chrome_frame/crash_reporting/nt_loader_unittest.cc
|
| diff --git a/chrome_frame/crash_reporting/nt_loader_unittest.cc b/chrome_frame/crash_reporting/nt_loader_unittest.cc
|
| deleted file mode 100644
|
| index d5576f389b8795856313ccf539235d8a2e88f64e..0000000000000000000000000000000000000000
|
| --- a/chrome_frame/crash_reporting/nt_loader_unittest.cc
|
| +++ /dev/null
|
| @@ -1,322 +0,0 @@
|
| -// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome_frame/crash_reporting/nt_loader.h"
|
| -
|
| -#include <tlhelp32.h>
|
| -#include <winnt.h>
|
| -
|
| -#include "base/at_exit.h"
|
| -#include "base/bind.h"
|
| -#include "base/bind_helpers.h"
|
| -#include "base/environment.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "base/sys_info.h"
|
| -#include "base/threading/thread.h"
|
| -#include "base/win/scoped_handle.h"
|
| -#include "chrome_frame/crash_reporting/crash_dll.h"
|
| -#include "gtest/gtest.h"
|
| -
|
| -namespace {
|
| -void AssertIsCriticalSection(CRITICAL_SECTION* critsec) {
|
| - // Assert on some of the internals of the debug info if it has one.
|
| - RTL_CRITICAL_SECTION_DEBUG* debug = critsec->DebugInfo;
|
| - if (debug) {
|
| - ASSERT_EQ(RTL_CRITSECT_TYPE, debug->Type);
|
| - ASSERT_EQ(critsec, debug->CriticalSection);
|
| - }
|
| -
|
| - // TODO(siggi): assert on the semaphore handle & object type?
|
| -}
|
| -
|
| -class ScopedEnterCriticalSection {
|
| - public:
|
| - explicit ScopedEnterCriticalSection(CRITICAL_SECTION* critsec)
|
| - : critsec_(critsec) {
|
| - ::EnterCriticalSection(critsec_);
|
| - }
|
| -
|
| - ~ScopedEnterCriticalSection() {
|
| - ::LeaveCriticalSection(critsec_);
|
| - }
|
| -
|
| - private:
|
| - CRITICAL_SECTION* critsec_;
|
| -};
|
| -
|
| -std::wstring FromUnicodeString(const UNICODE_STRING& str) {
|
| - return std::wstring(str.Buffer, str.Length / sizeof(str.Buffer[0]));
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -using namespace nt_loader;
|
| -
|
| -TEST(NtLoader, OwnsCriticalSection) {
|
| - // Use of Thread requires an atexit manager.
|
| - base::AtExitManager at_exit;
|
| -
|
| - CRITICAL_SECTION cs = {};
|
| - ::InitializeCriticalSection(&cs);
|
| - EXPECT_FALSE(OwnsCriticalSection(&cs));
|
| -
|
| - // Enter the critsec and assert we own it.
|
| - {
|
| - ScopedEnterCriticalSection lock1(&cs);
|
| -
|
| - EXPECT_TRUE(OwnsCriticalSection(&cs));
|
| -
|
| - // Re-enter the critsec and assert we own it.
|
| - ScopedEnterCriticalSection lock2(&cs);
|
| -
|
| - EXPECT_TRUE(OwnsCriticalSection(&cs));
|
| - }
|
| -
|
| - // Should no longer own it.
|
| - EXPECT_FALSE(OwnsCriticalSection(&cs));
|
| -
|
| - // Make another thread grab it.
|
| - base::Thread other("Other threads");
|
| - ASSERT_TRUE(other.Start());
|
| - other.message_loop()->PostTask(
|
| - FROM_HERE, base::Bind(::EnterCriticalSection, &cs));
|
| -
|
| - base::win::ScopedHandle event(::CreateEvent(NULL, FALSE, FALSE, NULL));
|
| - other.message_loop()->PostTask(
|
| - FROM_HERE, base::Bind(base::IgnoreResult(::SetEvent), event.Get()));
|
| -
|
| - ASSERT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(event.Get(), INFINITE));
|
| -
|
| - // We still shouldn't own it - the other thread does.
|
| - EXPECT_FALSE(OwnsCriticalSection(&cs));
|
| - // And we shouldn't be able to enter it.
|
| - EXPECT_EQ(0, ::TryEnterCriticalSection(&cs));
|
| -
|
| - // Make the other thread release it.
|
| - other.message_loop()->PostTask(
|
| - FROM_HERE, base::Bind(::LeaveCriticalSection, &cs));
|
| -
|
| - other.Stop();
|
| -
|
| - ::DeleteCriticalSection(&cs);
|
| -}
|
| -
|
| -TEST(NtLoader, GetLoaderLock) {
|
| - CRITICAL_SECTION* loader_lock = GetLoaderLock();
|
| -
|
| - AssertIsCriticalSection(loader_lock);
|
| -
|
| - // We should be able to enter and leave the loader's lock without trouble.
|
| - EnterCriticalSection(loader_lock);
|
| - LeaveCriticalSection(loader_lock);
|
| -}
|
| -
|
| -TEST(NtLoader, OwnsLoaderLock) {
|
| - CRITICAL_SECTION* loader_lock = GetLoaderLock();
|
| -
|
| - EXPECT_FALSE(OwnsLoaderLock());
|
| - EnterCriticalSection(loader_lock);
|
| - EXPECT_TRUE(OwnsLoaderLock());
|
| - LeaveCriticalSection(loader_lock);
|
| - EXPECT_FALSE(OwnsLoaderLock());
|
| -}
|
| -
|
| -TEST(NtLoader, GetLoaderEntry) {
|
| - // Get all modules in the current process.
|
| - base::win::ScopedHandle snap(
|
| - ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ::GetCurrentProcessId()));
|
| - EXPECT_TRUE(snap.Get() != NULL);
|
| -
|
| - // Walk them, while checking we get an entry for each, and that it
|
| - // contains sane information.
|
| - MODULEENTRY32 module = { sizeof(module) };
|
| - ASSERT_TRUE(::Module32First(snap.Get(), &module));
|
| - do {
|
| - ScopedEnterCriticalSection lock(GetLoaderLock());
|
| -
|
| - nt_loader::LDR_DATA_TABLE_ENTRY* entry =
|
| - nt_loader::GetLoaderEntry(module.hModule);
|
| - ASSERT_TRUE(entry != NULL);
|
| - EXPECT_EQ(module.hModule, reinterpret_cast<HMODULE>(entry->DllBase));
|
| - EXPECT_STREQ(module.szModule,
|
| - FromUnicodeString(entry->BaseDllName).c_str());
|
| - EXPECT_STREQ(module.szExePath,
|
| - FromUnicodeString(entry->FullDllName).c_str());
|
| -
|
| - ULONG flags = entry->Flags;
|
| -
|
| - // All entries should have this flag set.
|
| - EXPECT_TRUE(flags & LDRP_ENTRY_PROCESSED);
|
| -
|
| - if (0 == (flags & LDRP_IMAGE_DLL)) {
|
| - // TODO(siggi): write a test to assert this holds true for loading
|
| - // non-DLL, e.g. exe image files.
|
| - // Dlls have the LDRP_IMAGE_DLL flag set, any module that doesn't
|
| - // have that flag has to be the main executable.
|
| - EXPECT_TRUE(module.hModule == ::GetModuleHandle(NULL));
|
| - } else {
|
| - // Since we're not currently loading any modules, all loaded
|
| - // modules should either have the LDRP_PROCESS_ATTACH_CALLED,
|
| - // or a NULL entrypoint.
|
| - if (entry->EntryPoint == NULL) {
|
| - EXPECT_FALSE(flags & LDRP_PROCESS_ATTACH_CALLED);
|
| - } else {
|
| - // Shimeng.dll is an exception to the above, it's loaded
|
| - // in a special way, see e.g. http://www.alex-ionescu.com/?p=41
|
| - // for details.
|
| - bool is_shimeng = LowerCaseEqualsASCII(
|
| - FromUnicodeString(entry->BaseDllName), "shimeng.dll");
|
| -
|
| - EXPECT_TRUE(is_shimeng || (flags & LDRP_PROCESS_ATTACH_CALLED));
|
| - }
|
| - }
|
| - } while (::Module32Next(snap.Get(), &module));
|
| -}
|
| -
|
| -namespace {
|
| -
|
| -typedef void (*ExceptionFunction)(EXCEPTION_POINTERS* ex_ptrs);
|
| -
|
| -class NtLoaderTest: public testing::Test {
|
| - public:
|
| - NtLoaderTest() : veh_id_(NULL), exception_function_(NULL) {
|
| - EXPECT_EQ(NULL, current_);
|
| - current_ = this;
|
| - }
|
| -
|
| - ~NtLoaderTest() {
|
| - EXPECT_TRUE(this == current_);
|
| - current_ = NULL;
|
| - }
|
| -
|
| - void SetUp() {
|
| - veh_id_ = ::AddVectoredExceptionHandler(FALSE, &ExceptionHandler);
|
| - EXPECT_TRUE(veh_id_ != NULL);
|
| -
|
| - // Clear the crash DLL environment.
|
| - scoped_ptr<base::Environment> env(base::Environment::Create());
|
| - env->UnSetVar(WideToASCII(kCrashOnLoadMode).c_str());
|
| - env->UnSetVar(WideToASCII(kCrashOnUnloadMode).c_str());
|
| - }
|
| -
|
| - void TearDown() {
|
| - if (veh_id_ != NULL)
|
| - EXPECT_NE(0, ::RemoveVectoredExceptionHandler(veh_id_));
|
| -
|
| - // Clear the crash DLL environment.
|
| - scoped_ptr<base::Environment> env(base::Environment::Create());
|
| - env->UnSetVar(WideToASCII(kCrashOnLoadMode).c_str());
|
| - env->UnSetVar(WideToASCII(kCrashOnUnloadMode).c_str());
|
| - }
|
| -
|
| - void set_exception_function(ExceptionFunction func) {
|
| - exception_function_ = func;
|
| - }
|
| -
|
| - private:
|
| - static LONG NTAPI ExceptionHandler(EXCEPTION_POINTERS* ex_ptrs){
|
| - // Dispatch the exception to any exception function,
|
| - // but only on the main thread.
|
| - if (main_thread_ == ::GetCurrentThreadId() &&
|
| - current_ != NULL &&
|
| - current_->exception_function_ != NULL)
|
| - current_->exception_function_(ex_ptrs);
|
| -
|
| - return ExceptionContinueSearch;
|
| - }
|
| -
|
| - void* veh_id_;
|
| - ExceptionFunction exception_function_;
|
| -
|
| - static NtLoaderTest* current_;
|
| - static DWORD main_thread_;
|
| -};
|
| -
|
| -NtLoaderTest* NtLoaderTest::current_ = NULL;
|
| -DWORD NtLoaderTest::main_thread_ = ::GetCurrentThreadId();
|
| -
|
| -} // namespace
|
| -
|
| -static int exceptions_handled = 0;
|
| -static void OnCrashDuringLoadLibrary(EXCEPTION_POINTERS* ex_ptrs) {
|
| - ASSERT_EQ(STATUS_ACCESS_VIOLATION, ex_ptrs->ExceptionRecord->ExceptionCode);
|
| - ASSERT_EQ(2, ex_ptrs->ExceptionRecord->NumberParameters);
|
| - ASSERT_EQ(EXCEPTION_WRITE_FAULT,
|
| - ex_ptrs->ExceptionRecord->ExceptionInformation[0]);
|
| - ASSERT_EQ(kCrashAddress,
|
| - ex_ptrs->ExceptionRecord->ExceptionInformation[1]);
|
| -
|
| - // Bump the exceptions count.
|
| - exceptions_handled++;
|
| -
|
| - EXPECT_TRUE(OwnsLoaderLock());
|
| -
|
| - HMODULE crash_dll = ::GetModuleHandle(kCrashDllName);
|
| - ASSERT_TRUE(crash_dll != NULL);
|
| -
|
| - nt_loader::LDR_DATA_TABLE_ENTRY* entry = GetLoaderEntry(crash_dll);
|
| - ASSERT_TRUE(entry != NULL);
|
| - ASSERT_EQ(0, entry->Flags & LDRP_PROCESS_ATTACH_CALLED);
|
| -}
|
| -
|
| -TEST_F(NtLoaderTest, CrashOnLoadLibrary) {
|
| - exceptions_handled = 0;
|
| - set_exception_function(OnCrashDuringLoadLibrary);
|
| -
|
| - // Setup to crash on load.
|
| - scoped_ptr<base::Environment> env(base::Environment::Create());
|
| - env->SetVar(WideToASCII(kCrashOnLoadMode).c_str(), "1");
|
| -
|
| - // And load it.
|
| - HMODULE module = ::LoadLibrary(kCrashDllName);
|
| - DWORD err = ::GetLastError();
|
| - EXPECT_EQ(NULL, module);
|
| - EXPECT_EQ(ERROR_NOACCESS, err);
|
| - EXPECT_EQ(1, exceptions_handled);
|
| -
|
| - if (module != NULL)
|
| - ::FreeLibrary(module);
|
| -}
|
| -
|
| -static void OnCrashDuringUnloadLibrary(EXCEPTION_POINTERS* ex_ptrs) {
|
| - ASSERT_EQ(STATUS_ACCESS_VIOLATION, ex_ptrs->ExceptionRecord->ExceptionCode);
|
| - ASSERT_EQ(2, ex_ptrs->ExceptionRecord->NumberParameters);
|
| - ASSERT_EQ(EXCEPTION_WRITE_FAULT,
|
| - ex_ptrs->ExceptionRecord->ExceptionInformation[0]);
|
| - ASSERT_EQ(kCrashAddress,
|
| - ex_ptrs->ExceptionRecord->ExceptionInformation[1]);
|
| -
|
| - // Bump the exceptions count.
|
| - exceptions_handled++;
|
| -
|
| - EXPECT_TRUE(OwnsLoaderLock());
|
| -
|
| - HMODULE crash_dll = ::GetModuleHandle(kCrashDllName);
|
| - ASSERT_TRUE(crash_dll == NULL);
|
| -
|
| - nt_loader::LDR_DATA_TABLE_ENTRY* entry = GetLoaderEntry(crash_dll);
|
| - ASSERT_TRUE(entry == NULL);
|
| -}
|
| -
|
| -TEST_F(NtLoaderTest, CrashOnUnloadLibrary) {
|
| - // Setup to crash on unload.
|
| - scoped_ptr<base::Environment> env(base::Environment::Create());
|
| - env->SetVar(WideToASCII(kCrashOnUnloadMode).c_str(), "1");
|
| -
|
| - // And load it.
|
| - HMODULE module = ::LoadLibrary(kCrashDllName);
|
| - EXPECT_TRUE(module != NULL);
|
| -
|
| - exceptions_handled = 0;
|
| - set_exception_function(OnCrashDuringUnloadLibrary);
|
| -
|
| - // We should crash during unload.
|
| - if (module != NULL)
|
| - ::FreeLibrary(module);
|
| -
|
| - EXPECT_EQ(1, exceptions_handled);
|
| -}
|
|
|