<commit_before>// 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.

// This test validates that the ProcessSingleton class properly makes sure
// that there is only one main browser process.
//
// It is currently compiled and run on Windows and Posix(non-Mac) platforms.
// Mac uses system services and ProcessSingletonMac is a noop.  (Maybe it still
// makes sense to test that the system services are giving the behavior we
// want?)

#include <list>

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/scoped_temp_dir.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/test_launcher_utils.h"
#include "chrome/test/ui/ui_test.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// This is for the code that is to be ran in multiple threads at once,
// to stress a race condition on first process start.
// We use the thread safe ref counted base class so that we can use the
// NewRunnableMethod class to run the StartChrome methods in many threads.
class ChromeStarter : public base::RefCountedThreadSafe<ChromeStarter> {
 public:
  ChromeStarter(int timeout_ms, const FilePath& user_data_dir)
      : ready_event_(false /* manual */, false /* signaled */),
        done_event_(false /* manual */, false /* signaled */),
        process_handle_(base::kNullProcessHandle),
        process_terminated_(false),
        timeout_ms_(timeout_ms),
        user_data_dir_(user_data_dir) {
  }

  // We must reset some data members since we reuse the same ChromeStarter
  // object and start/stop it a few times. We must start fresh! :-)
  void Reset() {
    ready_event_.Reset();
    done_event_.Reset();
    if (process_handle_ != base::kNullProcessHandle)
      base::CloseProcessHandle(process_handle_);
    process_handle_ = base::kNullProcessHandle;
    process_terminated_ = false;
  }

  void StartChrome(base::WaitableEvent* start_event, bool first_run) {
    // TODO(mattm): maybe stuff should be refactored to use
    // UITest::LaunchBrowserHelper somehow?
    FilePath browser_directory;
    PathService::Get(chrome::DIR_APP, &browser_directory);
    CommandLine command_line(browser_directory.Append(
        chrome::kBrowserProcessExecutablePath));

    command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir_);

    if (first_run)
      command_line.AppendSwitch(switches::kFirstRun);
    else
      command_line.AppendSwitch(switches::kNoFirstRun);

    // Add the normal test-mode switches, except for the ones we're adding
    // ourselves.
    CommandLine standard_switches(CommandLine::NO_PROGRAM);
    test_launcher_utils::PrepareBrowserCommandLineForTests(&standard_switches);
    const CommandLine::SwitchMap& switch_map = standard_switches.GetSwitches();
    for (CommandLine::SwitchMap::const_iterator i = switch_map.begin();
         i != switch_map.end(); ++i) {
      const std::string& switch_name = i->first;
      if (switch_name == switches::kUserDataDir ||
          switch_name == switches::kFirstRun ||
          switch_name == switches::kNoFirstRun)
        continue;

      command_line.AppendSwitchNative(switch_name, i->second);
    }

    // Try to get all threads to launch the app at the same time.
    // So let the test know we are ready.
    ready_event_.Signal();
    // And then wait for the test to tell us to GO!
    ASSERT_NE(static_cast<base::WaitableEvent*>(NULL), start_event);
    start_event->Wait();

    // Here we don't wait for the app to be terminated because one of the
    // process will stay alive while the others will be restarted. If we would
    // wait here, we would never get a handle to the main process...
    base::LaunchProcess(command_line, base::LaunchOptions(), &process_handle_);
    ASSERT_NE(base::kNullProcessHandle, process_handle_);

    // We can wait on the handle here, we should get stuck on one and only
    // one process. The test below will take care of killing that process
    // to unstuck us once it confirms there is only one.
    process_terminated_ = base::WaitForSingleProcess(process_handle_,
                                                     timeout_ms_);
    // Let the test know we are done.
    done_event_.Signal();
  }

  // Public access to simplify the test code using them.
  base::WaitableEvent ready_event_;
  base::WaitableEvent done_event_;
  base::ProcessHandle process_handle_;
  bool process_terminated_;

 private:
  friend class base::RefCountedThreadSafe<ChromeStarter>;

  ~ChromeStarter() {
    if (process_handle_ != base::kNullProcessHandle)
      base::CloseProcessHandle(process_handle_);
  }

  int timeout_ms_;
  FilePath user_data_dir_;

  DISALLOW_COPY_AND_ASSIGN(ChromeStarter);
};

// Our test fixture that initializes and holds onto a few global vars.
class ProcessSingletonTest : public UITest {
 public:
  ProcessSingletonTest()
      // We use a manual reset so that all threads wake up at once when signaled
      // and thus we must manually reset it for each attempt.
      : threads_waker_(true /* manual */, false /* signaled */) {
    EXPECT_TRUE(temp_profile_dir_.CreateUniqueTempDir());
  }

  void SetUp() {
    // Start the threads and create the starters.
    for (size_t i = 0; i < kNbThreads; ++i) {
      chrome_starter_threads_[i].reset(new base::Thread("ChromeStarter"));
      ASSERT_TRUE(chrome_starter_threads_[i]->Start());
      chrome_starters_[i] = new ChromeStarter(
          TestTimeouts::action_max_timeout_ms(), temp_profile_dir_.path());
    }
  }

  void TearDown() {
    // Stop the threads.
    for (size_t i = 0; i < kNbThreads; ++i)
      chrome_starter_threads_[i]->Stop();
  }

  // This method is used to make sure we kill the main browser process after
  // all of its child processes have successfully attached to it. This was added
  // when we realized that if we just kill the parent process right away, we
  // sometimes end up with dangling child processes. If we Sleep for a certain
  // amount of time, we are OK... So we introduced this method to avoid a
  // flaky wait. Instead, we kill all descendants of the main process after we
  // killed it, relying on the fact that we can still get the parent id of a
  // child process, even when the parent dies.
  void KillProcessTree(base::ProcessHandle process_handle) {
    class ProcessTreeFilter : public base::ProcessFilter {
     public:
      explicit ProcessTreeFilter(base::ProcessId parent_pid) {
        ancestor_pids_.insert(parent_pid);
      }
      virtual bool Includes(const base::ProcessEntry & entry) const {
        if (ancestor_pids_.find(entry.parent_pid()) != ancestor_pids_.end()) {
          ancestor_pids_.insert(entry.pid());
          return true;
        } else {
          return false;
        }
      }
     private:
      mutable std::set<base::ProcessId> ancestor_pids_;
    } process_tree_filter(base::GetProcId(process_handle));

    // Start by explicitly killing the main process we know about...
    static const int kExitCode = 42;
    EXPECT_TRUE(base::KillProcess(process_handle, kExitCode, true /* wait */));

    // Then loop until we can't find any of its descendant.
    // But don't try more than kNbTries times...
    static const int kNbTries = 10;
    int num_tries = 0;
    while (base::GetProcessCount(chrome::kBrowserProcessExecutablePath,
        &process_tree_filter) > 0 && num_tries++ < kNbTries) {
      base::KillProcesses(chrome::kBrowserProcessExecutablePath,
                          kExitCode, &process_tree_filter);
    }
    DLOG_IF(ERROR, num_tries >= kNbTries) << "Failed to kill all processes!";
  }

  // Since this is a hard to reproduce problem, we make a few attempts.
  // We stop the attempts at the first error, and when there are no errors,
  // we don't time-out of any wait, so it executes quite fast anyway.
  static const size_t kNbAttempts = 5;

  // The idea is to start chrome from multiple threads all at once.
  static const size_t kNbThreads = 5;
  scoped_refptr<ChromeStarter> chrome_starters_[kNbThreads];
  scoped_ptr<base::Thread> chrome_starter_threads_[kNbThreads];

  // The event that will get all threads to wake up simultaneously and try
  // to start a chrome process at the same time.
  base::WaitableEvent threads_waker_;

  // We don't want to use the default profile, but can't use UITest's since we
  // don't use UITest::LaunchBrowser.
  ScopedTempDir temp_profile_dir_;
};

#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
// http://crbug.com/58219
#define MAYBE_StartupRaceCondition FAILS_StartupRaceCondition
#else
#define MAYBE_StartupRaceCondition StartupRaceCondition
#endif
TEST_F(ProcessSingletonTest, MAYBE_StartupRaceCondition) {
  // We use this to stop the attempts loop on the first failure.
  bool failed = false;
  for (size_t attempt = 0; attempt < kNbAttempts && !failed; ++attempt) {
    SCOPED_TRACE(testing::Message() << "Attempt: " << attempt << ".");
    // We use a single event to get all threads to do the AppLaunch at the same
    // time...
    threads_waker_.Reset();

    // Test both with and without the first-run dialog, since they exercise
    // different paths.
#if defined(OS_POSIX)
    // TODO(mattm): test first run dialog singleton handling on linux too.
    // On posix if we test the first run dialog, GracefulShutdownHandler gets
    // the TERM signal, but since the message loop isn't running during the gtk
    // first run dialog, the ShutdownDetector never handles it, and KillProcess
    // has to time out (60 sec!) and SIGKILL.
    bool first_run = false;
#else
    // Test for races in both regular start up and first run start up cases.
    bool first_run = attempt % 2;
#endif

    // Here we prime all the threads with a ChromeStarter that will wait for
    // our signal to launch its chrome process.
    for (size_t i = 0; i < kNbThreads; ++i) {
      ASSERT_NE(static_cast<ChromeStarter*>(NULL), chrome_starters_[i].get());
      chrome_starters_[i]->Reset();

      ASSERT_TRUE(chrome_starter_threads_[i]->IsRunning());
      ASSERT_NE(static_cast<MessageLoop*>(NULL),
                chrome_starter_threads_[i]->message_loop());

      chrome_starter_threads_[i]->message_loop()->PostTask(
          FROM_HERE, NewRunnableMethod(chrome_starters_[i].get(),
                                       &ChromeStarter::StartChrome,
                                       &threads_waker_,
                                       first_run));
    }

    // Wait for all the starters to be ready.
    // We could replace this loop if we ever implement a WaitAll().
    for (size_t i = 0; i < kNbThreads; ++i) {
      SCOPED_TRACE(testing::Message() << "Waiting on thread: " << i << ".");
      chrome_starters_[i]->ready_event_.Wait();
    }
    // GO!
    threads_waker_.Signal();

    // As we wait for all threads to signal that they are done, we remove their
    // index from this vector so that we get left with only the index of
    // the thread that started the main process.
    std::vector<size_t> pending_starters(kNbThreads);
    for (size_t i = 0; i < kNbThreads; ++i)
      pending_starters[i] = i;

    // We use a local array of starter's done events we must wait on...
    // These are collected from the starters that we have not yet been removed
    // from the pending_starters vector.
    base::WaitableEvent* starters_done_events[kNbThreads];
    // At the end, "There can be only one" main browser process alive.
    while (pending_starters.size() > 1) {
      SCOPED_TRACE(testing::Message() << pending_starters.size() <<
                   " starters left.");
      for (size_t i = 0; i < pending_starters.size(); ++i) {
        starters_done_events[i] =
            &chrome_starters_[pending_starters[i]]->done_event_;
      }
      size_t done_index = base::WaitableEvent::WaitMany(
          starters_done_events, pending_starters.size());
      size_t starter_index = pending_starters[done_index];
      // If the starter is done but has not marked itself as terminated,
      // it is because it timed out of its WaitForSingleProcess(). Only the
      // last one standing should be left waiting... So we failed...
      EXPECT_TRUE(chrome_starters_[starter_index]->process_terminated_ ||
                  failed) << "There is more than one main process.";
      if (!chrome_starters_[starter_index]->process_terminated_) {
        // This will stop the "for kNbAttempts" loop.
        failed = true;
        // But we let the last loop turn finish so that we can properly
        // kill all remaining processes. Starting with this one...
        if (chrome_starters_[starter_index]->process_handle_ !=
            base::kNullProcessHandle) {
          KillProcessTree(chrome_starters_[starter_index]->process_handle_);
        }
      }
      pending_starters.erase(pending_starters.begin() + done_index);
    }

    // "There can be only one!" :-)
    ASSERT_EQ(static_cast<size_t>(1), pending_starters.size());
    size_t last_index = pending_starters.front();
    pending_starters.empty();
    if (chrome_starters_[last_index]->process_handle_ !=
        base::kNullProcessHandle) {
      KillProcessTree(chrome_starters_[last_index]->process_handle_);
      chrome_starters_[last_index]->done_event_.Wait();
    }
  }
}

}  // namespace
<commit_msg>base::Bind() conversion for ProcessSingletonTest<commit_after>// 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.

// This test validates that the ProcessSingleton class properly makes sure
// that there is only one main browser process.
//
// It is currently compiled and run on Windows and Posix(non-Mac) platforms.
// Mac uses system services and ProcessSingletonMac is a noop.  (Maybe it still
// makes sense to test that the system services are giving the behavior we
// want?)

#include <list>

#include "base/bind.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/scoped_temp_dir.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/test_launcher_utils.h"
#include "chrome/test/ui/ui_test.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// This is for the code that is to be ran in multiple threads at once,
// to stress a race condition on first process start.
// We use the thread safe ref counted base class so that we can use the
// base::Bind to run the StartChrome methods in many threads.
class ChromeStarter : public base::RefCountedThreadSafe<ChromeStarter> {
 public:
  ChromeStarter(int timeout_ms, const FilePath& user_data_dir)
      : ready_event_(false /* manual */, false /* signaled */),
        done_event_(false /* manual */, false /* signaled */),
        process_handle_(base::kNullProcessHandle),
        process_terminated_(false),
        timeout_ms_(timeout_ms),
        user_data_dir_(user_data_dir) {
  }

  // We must reset some data members since we reuse the same ChromeStarter
  // object and start/stop it a few times. We must start fresh! :-)
  void Reset() {
    ready_event_.Reset();
    done_event_.Reset();
    if (process_handle_ != base::kNullProcessHandle)
      base::CloseProcessHandle(process_handle_);
    process_handle_ = base::kNullProcessHandle;
    process_terminated_ = false;
  }

  void StartChrome(base::WaitableEvent* start_event, bool first_run) {
    // TODO(mattm): maybe stuff should be refactored to use
    // UITest::LaunchBrowserHelper somehow?
    FilePath browser_directory;
    PathService::Get(chrome::DIR_APP, &browser_directory);
    CommandLine command_line(browser_directory.Append(
        chrome::kBrowserProcessExecutablePath));

    command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir_);

    if (first_run)
      command_line.AppendSwitch(switches::kFirstRun);
    else
      command_line.AppendSwitch(switches::kNoFirstRun);

    // Add the normal test-mode switches, except for the ones we're adding
    // ourselves.
    CommandLine standard_switches(CommandLine::NO_PROGRAM);
    test_launcher_utils::PrepareBrowserCommandLineForTests(&standard_switches);
    const CommandLine::SwitchMap& switch_map = standard_switches.GetSwitches();
    for (CommandLine::SwitchMap::const_iterator i = switch_map.begin();
         i != switch_map.end(); ++i) {
      const std::string& switch_name = i->first;
      if (switch_name == switches::kUserDataDir ||
          switch_name == switches::kFirstRun ||
          switch_name == switches::kNoFirstRun)
        continue;

      command_line.AppendSwitchNative(switch_name, i->second);
    }

    // Try to get all threads to launch the app at the same time.
    // So let the test know we are ready.
    ready_event_.Signal();
    // And then wait for the test to tell us to GO!
    ASSERT_NE(static_cast<base::WaitableEvent*>(NULL), start_event);
    start_event->Wait();

    // Here we don't wait for the app to be terminated because one of the
    // process will stay alive while the others will be restarted. If we would
    // wait here, we would never get a handle to the main process...
    base::LaunchProcess(command_line, base::LaunchOptions(), &process_handle_);
    ASSERT_NE(base::kNullProcessHandle, process_handle_);

    // We can wait on the handle here, we should get stuck on one and only
    // one process. The test below will take care of killing that process
    // to unstuck us once it confirms there is only one.
    process_terminated_ = base::WaitForSingleProcess(process_handle_,
                                                     timeout_ms_);
    // Let the test know we are done.
    done_event_.Signal();
  }

  // Public access to simplify the test code using them.
  base::WaitableEvent ready_event_;
  base::WaitableEvent done_event_;
  base::ProcessHandle process_handle_;
  bool process_terminated_;

 private:
  friend class base::RefCountedThreadSafe<ChromeStarter>;

  ~ChromeStarter() {
    if (process_handle_ != base::kNullProcessHandle)
      base::CloseProcessHandle(process_handle_);
  }

  int timeout_ms_;
  FilePath user_data_dir_;

  DISALLOW_COPY_AND_ASSIGN(ChromeStarter);
};

// Our test fixture that initializes and holds onto a few global vars.
class ProcessSingletonTest : public UITest {
 public:
  ProcessSingletonTest()
      // We use a manual reset so that all threads wake up at once when signaled
      // and thus we must manually reset it for each attempt.
      : threads_waker_(true /* manual */, false /* signaled */) {
    EXPECT_TRUE(temp_profile_dir_.CreateUniqueTempDir());
  }

  void SetUp() {
    // Start the threads and create the starters.
    for (size_t i = 0; i < kNbThreads; ++i) {
      chrome_starter_threads_[i].reset(new base::Thread("ChromeStarter"));
      ASSERT_TRUE(chrome_starter_threads_[i]->Start());
      chrome_starters_[i] = new ChromeStarter(
          TestTimeouts::action_max_timeout_ms(), temp_profile_dir_.path());
    }
  }

  void TearDown() {
    // Stop the threads.
    for (size_t i = 0; i < kNbThreads; ++i)
      chrome_starter_threads_[i]->Stop();
  }

  // This method is used to make sure we kill the main browser process after
  // all of its child processes have successfully attached to it. This was added
  // when we realized that if we just kill the parent process right away, we
  // sometimes end up with dangling child processes. If we Sleep for a certain
  // amount of time, we are OK... So we introduced this method to avoid a
  // flaky wait. Instead, we kill all descendants of the main process after we
  // killed it, relying on the fact that we can still get the parent id of a
  // child process, even when the parent dies.
  void KillProcessTree(base::ProcessHandle process_handle) {
    class ProcessTreeFilter : public base::ProcessFilter {
     public:
      explicit ProcessTreeFilter(base::ProcessId parent_pid) {
        ancestor_pids_.insert(parent_pid);
      }
      virtual bool Includes(const base::ProcessEntry & entry) const {
        if (ancestor_pids_.find(entry.parent_pid()) != ancestor_pids_.end()) {
          ancestor_pids_.insert(entry.pid());
          return true;
        } else {
          return false;
        }
      }
     private:
      mutable std::set<base::ProcessId> ancestor_pids_;
    } process_tree_filter(base::GetProcId(process_handle));

    // Start by explicitly killing the main process we know about...
    static const int kExitCode = 42;
    EXPECT_TRUE(base::KillProcess(process_handle, kExitCode, true /* wait */));

    // Then loop until we can't find any of its descendant.
    // But don't try more than kNbTries times...
    static const int kNbTries = 10;
    int num_tries = 0;
    while (base::GetProcessCount(chrome::kBrowserProcessExecutablePath,
        &process_tree_filter) > 0 && num_tries++ < kNbTries) {
      base::KillProcesses(chrome::kBrowserProcessExecutablePath,
                          kExitCode, &process_tree_filter);
    }
    DLOG_IF(ERROR, num_tries >= kNbTries) << "Failed to kill all processes!";
  }

  // Since this is a hard to reproduce problem, we make a few attempts.
  // We stop the attempts at the first error, and when there are no errors,
  // we don't time-out of any wait, so it executes quite fast anyway.
  static const size_t kNbAttempts = 5;

  // The idea is to start chrome from multiple threads all at once.
  static const size_t kNbThreads = 5;
  scoped_refptr<ChromeStarter> chrome_starters_[kNbThreads];
  scoped_ptr<base::Thread> chrome_starter_threads_[kNbThreads];

  // The event that will get all threads to wake up simultaneously and try
  // to start a chrome process at the same time.
  base::WaitableEvent threads_waker_;

  // We don't want to use the default profile, but can't use UITest's since we
  // don't use UITest::LaunchBrowser.
  ScopedTempDir temp_profile_dir_;
};

#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
// http://crbug.com/58219
#define MAYBE_StartupRaceCondition FAILS_StartupRaceCondition
#else
#define MAYBE_StartupRaceCondition StartupRaceCondition
#endif
TEST_F(ProcessSingletonTest, MAYBE_StartupRaceCondition) {
  // We use this to stop the attempts loop on the first failure.
  bool failed = false;
  for (size_t attempt = 0; attempt < kNbAttempts && !failed; ++attempt) {
    SCOPED_TRACE(testing::Message() << "Attempt: " << attempt << ".");
    // We use a single event to get all threads to do the AppLaunch at the same
    // time...
    threads_waker_.Reset();

    // Test both with and without the first-run dialog, since they exercise
    // different paths.
#if defined(OS_POSIX)
    // TODO(mattm): test first run dialog singleton handling on linux too.
    // On posix if we test the first run dialog, GracefulShutdownHandler gets
    // the TERM signal, but since the message loop isn't running during the gtk
    // first run dialog, the ShutdownDetector never handles it, and KillProcess
    // has to time out (60 sec!) and SIGKILL.
    bool first_run = false;
#else
    // Test for races in both regular start up and first run start up cases.
    bool first_run = attempt % 2;
#endif

    // Here we prime all the threads with a ChromeStarter that will wait for
    // our signal to launch its chrome process.
    for (size_t i = 0; i < kNbThreads; ++i) {
      ASSERT_NE(static_cast<ChromeStarter*>(NULL), chrome_starters_[i].get());
      chrome_starters_[i]->Reset();

      ASSERT_TRUE(chrome_starter_threads_[i]->IsRunning());
      ASSERT_NE(static_cast<MessageLoop*>(NULL),
                chrome_starter_threads_[i]->message_loop());

      chrome_starter_threads_[i]->message_loop()->PostTask(
          FROM_HERE, base::Bind(&ChromeStarter::StartChrome,
                                chrome_starters_[i].get(),
                                &threads_waker_,
                                first_run));
    }

    // Wait for all the starters to be ready.
    // We could replace this loop if we ever implement a WaitAll().
    for (size_t i = 0; i < kNbThreads; ++i) {
      SCOPED_TRACE(testing::Message() << "Waiting on thread: " << i << ".");
      chrome_starters_[i]->ready_event_.Wait();
    }
    // GO!
    threads_waker_.Signal();

    // As we wait for all threads to signal that they are done, we remove their
    // index from this vector so that we get left with only the index of
    // the thread that started the main process.
    std::vector<size_t> pending_starters(kNbThreads);
    for (size_t i = 0; i < kNbThreads; ++i)
      pending_starters[i] = i;

    // We use a local array of starter's done events we must wait on...
    // These are collected from the starters that we have not yet been removed
    // from the pending_starters vector.
    base::WaitableEvent* starters_done_events[kNbThreads];
    // At the end, "There can be only one" main browser process alive.
    while (pending_starters.size() > 1) {
      SCOPED_TRACE(testing::Message() << pending_starters.size() <<
                   " starters left.");
      for (size_t i = 0; i < pending_starters.size(); ++i) {
        starters_done_events[i] =
            &chrome_starters_[pending_starters[i]]->done_event_;
      }
      size_t done_index = base::WaitableEvent::WaitMany(
          starters_done_events, pending_starters.size());
      size_t starter_index = pending_starters[done_index];
      // If the starter is done but has not marked itself as terminated,
      // it is because it timed out of its WaitForSingleProcess(). Only the
      // last one standing should be left waiting... So we failed...
      EXPECT_TRUE(chrome_starters_[starter_index]->process_terminated_ ||
                  failed) << "There is more than one main process.";
      if (!chrome_starters_[starter_index]->process_terminated_) {
        // This will stop the "for kNbAttempts" loop.
        failed = true;
        // But we let the last loop turn finish so that we can properly
        // kill all remaining processes. Starting with this one...
        if (chrome_starters_[starter_index]->process_handle_ !=
            base::kNullProcessHandle) {
          KillProcessTree(chrome_starters_[starter_index]->process_handle_);
        }
      }
      pending_starters.erase(pending_starters.begin() + done_index);
    }

    // "There can be only one!" :-)
    ASSERT_EQ(static_cast<size_t>(1), pending_starters.size());
    size_t last_index = pending_starters.front();
    pending_starters.empty();
    if (chrome_starters_[last_index]->process_handle_ !=
        base::kNullProcessHandle) {
      KillProcessTree(chrome_starters_[last_index]->process_handle_);
      chrome_starters_[last_index]->done_event_.Wait();
    }
  }
}

}  // namespace
<|endoftext|>


<commit_before>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#ifndef FLUSSPFERD_XML_NODE_MAP_HPP
#define FLUSSPFERD_XML_NODE_MAP_HPP

#include <flusspferd.hpp>
#include <flusspferd/aliases.hpp>

#include <map>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <iostream>

namespace xml_plugin {

class node_map;
class document;
typedef boost::weak_ptr<node_map> weak_node_map;
typedef boost::shared_ptr<node_map> node_map_ptr;

class node_map : public boost::enable_shared_from_this<node_map> {
public:
  /*
   * So, how ever you get hold of a given node in a document, it should always be
   * the same object (in the JS sense):
   *
   *     node === node.firstChild.parentNode
   *
   * We store a map of void* (which is the underlying Node_impl* from Arabica)
   * against the object*. Any function which can return a node should first look
   * in this map, and if found should return the node from there.
   *
   * Each node should store a weak_ptr to the shared_node_map. In the node's
   * destructor it should see if the shared_node_map is alive -- if it is it
   * should remove itself from the map. This is to cope with the fact that
   * finalizers can happen in any order in spidermonkey (i.e. the document could
   * be finalized before the nodes)
   */
  typedef std::map<void*, flusspferd::object> node_identiy_map;
private:
  // Nothing can create instances of us directly
  node_map() {}
protected:
  friend class document;

  // Template this to avoid circular dep on document
  template <class T, class U>
  static node_map_ptr make(T &obj, U node) {
    node_map_ptr map(new node_map());
    void *ptr = static_cast<void*>(node.underlying_impl());
    map->node_map_[ptr] = obj;
    return map;
  }

  node_identiy_map node_map_;

public:

  ~node_map() {}

  template <class T, class U>
  flusspferd::object get_node(U node) {
    void *ptr = static_cast<void*>(node.underlying_impl());

    std::cout << "Looking for (" << int(ptr) << ") in the node_map" << std::endl;
    node_identiy_map::const_iterator it = node_map_.find(ptr);

    if (it == node_map_.end()) {
      using namespace flusspferd;
      using namespace flusspferd::aliases;

      object o = create<T>( make_vector( node, shared_from_this() ) );
      std::cout << "Put something (" << int(ptr) << ") in the node_map" << std::endl;
      node_map_[ptr] = o;
      return o;
    }

    // Should i use dynamic cast here? Probably, but for now, eh.
    std::cout << "Found (" << int(ptr) << ") in the node_map" << std::endl;
    return it->second;
  }

  template <class U>
  void remove_mapped_node(U node) {
    void *ptr = static_cast<void*>(node.underlying_impl());
    std::cout << "Removing (" << int(ptr) << ") from the node_map_" << std::endl;
    node_map_.erase(ptr);
  }
};

}

#endif
<commit_msg>plugin/xml: remove debug prints<commit_after>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#ifndef FLUSSPFERD_XML_NODE_MAP_HPP
#define FLUSSPFERD_XML_NODE_MAP_HPP

#include <flusspferd.hpp>
#include <flusspferd/aliases.hpp>

#include <map>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

//#include <iostream>

namespace xml_plugin {

class node_map;
class document;
typedef boost::weak_ptr<node_map> weak_node_map;
typedef boost::shared_ptr<node_map> node_map_ptr;

class node_map : public boost::enable_shared_from_this<node_map> {
public:
  /*
   * So, how ever you get hold of a given node in a document, it should always be
   * the same object (in the JS sense):
   *
   *     node === node.firstChild.parentNode
   *
   * We store a map of void* (which is the underlying Node_impl* from Arabica)
   * against the object*. Any function which can return a node should first look
   * in this map, and if found should return the node from there.
   *
   * Each node should store a weak_ptr to the shared_node_map. In the node's
   * destructor it should see if the shared_node_map is alive -- if it is it
   * should remove itself from the map. This is to cope with the fact that
   * finalizers can happen in any order in spidermonkey (i.e. the document could
   * be finalized before the nodes)
   */
  typedef std::map<void*, flusspferd::object> node_identiy_map;
private:
  // Nothing can create instances of us directly
  node_map() {}
protected:
  friend class document;

  // Template this to avoid circular dep on document
  template <class T, class U>
  static node_map_ptr make(T &obj, U node) {
    node_map_ptr map(new node_map());
    void *ptr = static_cast<void*>(node.underlying_impl());
    map->node_map_[ptr] = obj;
    return map;
  }

  node_identiy_map node_map_;

public:

  ~node_map() {}

  template <class T, class U>
  flusspferd::object get_node(U node) {
    void *ptr = static_cast<void*>(node.underlying_impl());

    //std::cout << "Looking for (" << int(ptr) << ") in the node_map" << std::endl;
    node_identiy_map::const_iterator it = node_map_.find(ptr);

    if (it == node_map_.end()) {
      using namespace flusspferd;
      using namespace flusspferd::aliases;

      object o = create<T>( make_vector( node, shared_from_this() ) );
      //std::cout << "Put something (" << int(ptr) << ") in the node_map" << std::endl;
      node_map_[ptr] = o;
      return o;
    }

    // Should i use dynamic cast here? Probably, but for now, eh.
    //std::cout << "Found (" << int(ptr) << ") in the node_map" << std::endl;
    return it->second;
  }

  template <class U>
  void remove_mapped_node(U node) {
    void *ptr = static_cast<void*>(node.underlying_impl());
    //std::cout << "Removing (" << int(ptr) << ") from the node_map_" << std::endl;
    node_map_.erase(ptr);
  }
};

}

#endif
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * Copyright (c) 2010 Kohei Yoshida
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 ************************************************************************/

#include "mdds/point_quad_tree.hpp"

#include <cstdint>
#include <algorithm>
#include <vector>
#include <sstream>
#include <boost/ptr_container/ptr_vector.hpp>

#include <stdio.h>
#include <string>
#include <sys/time.h>

namespace {

class StackPrinter
{
public:
    explicit StackPrinter(const char* msg) :
        msMsg(msg)
    {
        fprintf(stdout, "%s: --begin\n", msMsg.c_str());
        mfStartTime = getTime();
    }

    ~StackPrinter()
    {
        double fEndTime = getTime();
        fprintf(stdout, "%s: --end (duration: %g sec)\n", msMsg.c_str(), (fEndTime-mfStartTime));
    }

    void printTime(int line) const
    {
        double fEndTime = getTime();
        fprintf(stdout, "%s: --(%d) (duration: %g sec)\n", msMsg.c_str(), line, (fEndTime-mfStartTime));
    }

private:
    double getTime() const
    {
        timeval tv;
        gettimeofday(&tv, NULL);
        return tv.tv_sec + tv.tv_usec / 1000000.0;
    }

    ::std::string msMsg;
    double mfStartTime;
};

}

using namespace std;
using namespace mdds;
using ::boost::ptr_vector;

struct data_printer : public unary_function<string*, void>
{
    void operator() (const string* p)
    {
        cout << *p << " ";
    }
};

template<typename _DbType>
struct search_result_printer : public unary_function<pair<typename _DbType::point, typename _DbType::data_type*>, void>
{
    void operator() (const pair<const typename _DbType::point, const typename _DbType::data_type*>& r) const
    {
        cout << "  (x=" << r.first.x << ", y=" << r.first.y << ", value='" << *r.second << "')" << endl;
    }
};

template<typename _DbType>
bool verify_stored_data(
    const vector<typename _DbType::node_data>& stored_data, const vector<typename _DbType::node_data>& verify_data)
{
    typedef vector<typename _DbType::node_data> node_array_type;

    node_array_type v1 = stored_data, v2 = verify_data;
    return _DbType::equals(v1, v2);
}

void pqt_test_basic()
{
    StackPrinter __stack_printer__("::pqt_test");
    typedef point_quad_tree<uint16_t, string> db_type;
    db_type db;

    string A("A");
    string B("B");
    string C("C");
    string D("D");
    string E("E");
    string F("F");
    string G("G");
    string H("H");
    string I("I");
    string J("J");
    string K("K");
    string L("L");
    string M("M");
    string N("N");

    db.insert(25, 32, &A);
    db.insert( 5, 45, &B);
    db.insert(52, 10, &C);
    db.insert(80,  5, &D);
    db.insert(40, 50, &E);
    db.insert(10, 10, &F);
    db.insert(20, 20, &G);
    db.insert(80, 80, &H);
    db.insert(58, 46, &I);
    db.insert(36, 55, &J);
    db.insert(26, 52, &K);
    db.insert(38, 68, &L);
    db.insert(39, 78, &M);
    db.insert(72, 52, &N);

    assert(db.size() == 14);

    cout << "node count = " << get_node_instance_count() << endl;
    db.dump_tree_svg("./obj/test.svg");

    {
        db_type::data_array_type result;
        db.search_region(10, 10, 60, 20, result);
        cout << "search region: (10, 10, 60, 20)" << endl;
        cout << "result: ";
        for_each(result.begin(), result.end(), data_printer());
        cout << endl;

        result.clear();
        db.search_region(10, 10, 61, 61, result);
        cout << "search region: (10, 10, 61, 61)" << endl;
        cout << "result: ";
        for_each(result.begin(), result.end(), data_printer());
        cout << endl;
    }

    db_type::search_result result = db.search_region(10, 10, 60, 20);
    db_type::search_result::const_iterator itr = result.begin(), itr_end = result.end();
    cout << "result: " << endl;
    for_each(result.begin(), result.end(), search_result_printer<db_type>());

    result = db.search_region(10, 10, 61, 61);
    itr = result.begin(), itr_end = result.end();
    cout << "result: " << endl;
    for_each(result.begin(), result.end(), search_result_printer<db_type>());

    db.remove(20, 20);
    db.remove(40, 50);
    assert(db.size() == 12);
    db.dump_tree_svg("./obj/test-remove.svg");

    db.clear();
    assert(db.empty());
    assert(db.size() == 0);
}

void pqt_test_insertion()
{
    StackPrinter __stack_printer__("::pqt_test_insertion");
    typedef point_quad_tree<int32_t, string> db_type;
    db_type db;

    // Check its empty-ness...
    assert(db.empty());
    assert(db.size() == 0);

    // Create all data instances.
    ptr_vector<string> data_store;
    data_store.reserve(100);
    for (size_t i = 0; i < 100; ++i)
    {
        ostringstream os;
        os << "0x" << hex << i;
        data_store.push_back(new string(os.str()));
    }

    vector<db_type::node_data> verify_data;

    // Insert data one by one, and verify each insertion.
    for (int32_t i = 0; i < 10; ++i)
    {
        for (int32_t j = 0; j < 10; ++j)
        {
            int32_t x = i*10 + 1, y = j*10 + 1;
            size_t index = i*10 + j;
            string* data_ptr = &data_store[index];
            cout << "inserting '" << *data_ptr << "' at (" << x << "," << y << ")" << endl;
            db.insert(x, y, data_ptr);
            verify_data.push_back(db_type::node_data(x, y, data_ptr));

            vector<db_type::node_data> stored_data;
            db.get_all_stored_data(stored_data);
            assert(stored_data.size() == (index+1));
            assert(db.size() == (index+1));
            assert(!db.empty());
            bool success = verify_stored_data<db_type>(stored_data, verify_data);
            assert(success);
        }
    }
    db.dump_tree_svg("./obj/pqt_test_insertion.svg");

    // Remove data one by one, and check the size after each removal.
    size_t node_count = 100;
    for (int32_t i = 0; i < 10; ++i)
    {
        for (int32_t j = 0; j < 10; ++j)
        {
            int32_t x = i*10 + 1, y = j*10 + 1;
            cout << "removing node at (" << x << "," << y << ")" << endl;
            db.remove(x, y);
            size_t n = db.size();
            cout << "size after removal: " << n << endl;
            assert(--node_count == n);
        }
    }
}

void pqt_test_remove_root()
{
    StackPrinter __stack_printer__("::pqt_test_remove_root");
    typedef point_quad_tree<int32_t, string> db_type;
    string O("O");
    string NW("NW");
    string NE("NE");
    string SW("SW");
    string SE("SE");
    db_type db;

    // Insert all data and verify their storage.
    db.insert(10, 10, &O);
    db.insert(20, 0, &NE);
    db.insert(0, 0, &NW);
    db.insert(20, 20, &SE);
    db.insert(0, 20, &SW);
    db.dump_tree_svg("./obj/pqt_test_remove_root-1.svg");

    vector<db_type::node_data> verify_data, stored_data;
    verify_data.push_back(db_type::node_data(10, 10, &O));
    verify_data.push_back(db_type::node_data(20, 0, &NE));
    verify_data.push_back(db_type::node_data(0, 0, &NW));
    verify_data.push_back(db_type::node_data(20, 20, &SE));
    verify_data.push_back(db_type::node_data(0, 20, &SW));
    db.get_all_stored_data(stored_data);
    bool success = db_type::equals(verify_data, stored_data);
    assert(success);
    assert(db.size() == 5);

    // Now, remove the root node.
    db.remove(10, 10);
    db.dump_tree_svg("./obj/pqt_test_remove_root-2.svg");
    db.get_all_stored_data(stored_data);
    verify_data.clear();
    verify_data.push_back(db_type::node_data(20, 0, &NE));
    verify_data.push_back(db_type::node_data(0, 0, &NW));
    verify_data.push_back(db_type::node_data(20, 20, &SE));
    verify_data.push_back(db_type::node_data(0, 20, &SW));
    success = db_type::equals(verify_data, stored_data);
    assert(success);
    assert(db.size() == 4);
}

int main()
{
    pqt_test_basic();
    pqt_test_insertion();
    pqt_test_remove_root();
    assert(get_node_instance_count() == 0);
}
<commit_msg>Test case for equality and non-equality.<commit_after>/*************************************************************************
 *
 * Copyright (c) 2010 Kohei Yoshida
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 ************************************************************************/

#include "mdds/point_quad_tree.hpp"

#include <cstdint>
#include <algorithm>
#include <vector>
#include <sstream>
#include <boost/ptr_container/ptr_vector.hpp>

#include <stdio.h>
#include <string>
#include <sys/time.h>

namespace {

class StackPrinter
{
public:
    explicit StackPrinter(const char* msg) :
        msMsg(msg)
    {
        fprintf(stdout, "%s: --begin\n", msMsg.c_str());
        mfStartTime = getTime();
    }

    ~StackPrinter()
    {
        double fEndTime = getTime();
        fprintf(stdout, "%s: --end (duration: %g sec)\n", msMsg.c_str(), (fEndTime-mfStartTime));
    }

    void printTime(int line) const
    {
        double fEndTime = getTime();
        fprintf(stdout, "%s: --(%d) (duration: %g sec)\n", msMsg.c_str(), line, (fEndTime-mfStartTime));
    }

private:
    double getTime() const
    {
        timeval tv;
        gettimeofday(&tv, NULL);
        return tv.tv_sec + tv.tv_usec / 1000000.0;
    }

    ::std::string msMsg;
    double mfStartTime;
};

}

using namespace std;
using namespace mdds;
using ::boost::ptr_vector;

struct data_printer : public unary_function<string*, void>
{
    void operator() (const string* p)
    {
        cout << *p << " ";
    }
};

template<typename _DbType>
struct search_result_printer : public unary_function<pair<typename _DbType::point, typename _DbType::data_type*>, void>
{
    void operator() (const pair<const typename _DbType::point, const typename _DbType::data_type*>& r) const
    {
        cout << "  (x=" << r.first.x << ", y=" << r.first.y << ", value='" << *r.second << "')" << endl;
    }
};

template<typename _DbType>
bool verify_stored_data(
    const vector<typename _DbType::node_data>& stored_data, const vector<typename _DbType::node_data>& verify_data)
{
    typedef vector<typename _DbType::node_data> node_array_type;

    node_array_type v1 = stored_data, v2 = verify_data;
    return _DbType::equals(v1, v2);
}

void pqt_test_basic()
{
    StackPrinter __stack_printer__("::pqt_test");
    typedef point_quad_tree<uint16_t, string> db_type;
    db_type db;

    string A("A");
    string B("B");
    string C("C");
    string D("D");
    string E("E");
    string F("F");
    string G("G");
    string H("H");
    string I("I");
    string J("J");
    string K("K");
    string L("L");
    string M("M");
    string N("N");

    db.insert(25, 32, &A);
    db.insert( 5, 45, &B);
    db.insert(52, 10, &C);
    db.insert(80,  5, &D);
    db.insert(40, 50, &E);
    db.insert(10, 10, &F);
    db.insert(20, 20, &G);
    db.insert(80, 80, &H);
    db.insert(58, 46, &I);
    db.insert(36, 55, &J);
    db.insert(26, 52, &K);
    db.insert(38, 68, &L);
    db.insert(39, 78, &M);
    db.insert(72, 52, &N);

    assert(db.size() == 14);

    cout << "node count = " << get_node_instance_count() << endl;
    db.dump_tree_svg("./obj/test.svg");

    {
        db_type::data_array_type result;
        db.search_region(10, 10, 60, 20, result);
        cout << "search region: (10, 10, 60, 20)" << endl;
        cout << "result: ";
        for_each(result.begin(), result.end(), data_printer());
        cout << endl;

        result.clear();
        db.search_region(10, 10, 61, 61, result);
        cout << "search region: (10, 10, 61, 61)" << endl;
        cout << "result: ";
        for_each(result.begin(), result.end(), data_printer());
        cout << endl;
    }

    db_type::search_result result = db.search_region(10, 10, 60, 20);
    db_type::search_result::const_iterator itr = result.begin(), itr_end = result.end();
    cout << "result: " << endl;
    for_each(result.begin(), result.end(), search_result_printer<db_type>());

    result = db.search_region(10, 10, 61, 61);
    itr = result.begin(), itr_end = result.end();
    cout << "result: " << endl;
    for_each(result.begin(), result.end(), search_result_printer<db_type>());

    db.remove(20, 20);
    db.remove(40, 50);
    assert(db.size() == 12);
    db.dump_tree_svg("./obj/test-remove.svg");

    db.clear();
    assert(db.empty());
    assert(db.size() == 0);
}

void pqt_test_insertion()
{
    StackPrinter __stack_printer__("::pqt_test_insertion");
    typedef point_quad_tree<int32_t, string> db_type;
    db_type db;

    // Check its empty-ness...
    assert(db.empty());
    assert(db.size() == 0);

    // Create all data instances.
    ptr_vector<string> data_store;
    data_store.reserve(100);
    for (size_t i = 0; i < 100; ++i)
    {
        ostringstream os;
        os << "0x" << hex << i;
        data_store.push_back(new string(os.str()));
    }

    vector<db_type::node_data> verify_data;

    // Insert data one by one, and verify each insertion.
    for (int32_t i = 0; i < 10; ++i)
    {
        for (int32_t j = 0; j < 10; ++j)
        {
            int32_t x = i*10 + 1, y = j*10 + 1;
            size_t index = i*10 + j;
            string* data_ptr = &data_store[index];
            cout << "inserting '" << *data_ptr << "' at (" << x << "," << y << ")" << endl;
            db.insert(x, y, data_ptr);
            verify_data.push_back(db_type::node_data(x, y, data_ptr));

            vector<db_type::node_data> stored_data;
            db.get_all_stored_data(stored_data);
            assert(stored_data.size() == (index+1));
            assert(db.size() == (index+1));
            assert(!db.empty());
            bool success = verify_stored_data<db_type>(stored_data, verify_data);
            assert(success);
        }
    }
    db.dump_tree_svg("./obj/pqt_test_insertion.svg");

    // Remove data one by one, and check the size after each removal.
    size_t node_count = 100;
    for (int32_t i = 0; i < 10; ++i)
    {
        for (int32_t j = 0; j < 10; ++j)
        {
            int32_t x = i*10 + 1, y = j*10 + 1;
            cout << "removing node at (" << x << "," << y << ")" << endl;
            db.remove(x, y);
            size_t n = db.size();
            cout << "size after removal: " << n << endl;
            assert(--node_count == n);
        }
    }
}

void pqt_test_remove_root()
{
    StackPrinter __stack_printer__("::pqt_test_remove_root");
    typedef point_quad_tree<int32_t, string> db_type;
    string O("O");
    string NW("NW");
    string NE("NE");
    string SW("SW");
    string SE("SE");
    db_type db;

    // Insert all data and verify their storage.
    db.insert(10, 10, &O);
    db.insert(20, 0, &NE);
    db.insert(0, 0, &NW);
    db.insert(20, 20, &SE);
    db.insert(0, 20, &SW);
    db.dump_tree_svg("./obj/pqt_test_remove_root-1.svg");

    vector<db_type::node_data> verify_data, stored_data;
    verify_data.push_back(db_type::node_data(10, 10, &O));
    verify_data.push_back(db_type::node_data(20, 0, &NE));
    verify_data.push_back(db_type::node_data(0, 0, &NW));
    verify_data.push_back(db_type::node_data(20, 20, &SE));
    verify_data.push_back(db_type::node_data(0, 20, &SW));
    db.get_all_stored_data(stored_data);
    bool success = db_type::equals(verify_data, stored_data);
    assert(success);
    assert(db.size() == 5);

    // Now, remove the root node.
    db.remove(10, 10);
    db.dump_tree_svg("./obj/pqt_test_remove_root-2.svg");
    db.get_all_stored_data(stored_data);
    verify_data.clear();
    verify_data.push_back(db_type::node_data(20, 0, &NE));
    verify_data.push_back(db_type::node_data(0, 0, &NW));
    verify_data.push_back(db_type::node_data(20, 20, &SE));
    verify_data.push_back(db_type::node_data(0, 20, &SW));
    success = db_type::equals(verify_data, stored_data);
    assert(success);
    assert(db.size() == 4);
}

void pqt_test_equality()
{
    StackPrinter __stack_printer__("::pqt_test_equality");

    typedef point_quad_tree<int32_t, string> db_type;
    db_type db1, db2;

    string A("A");
    string B("B");
    string C("C");
    string D("D");
    string E("E");
    string F("F");

    assert(db1 == db2); // both are empty.

    db1.insert(0, 0, &A);
    db2.insert(0, 0, &A);
    assert(db1 == db2);
    db1.remove(0, 0);
    assert(db1 != db2);
    db1.insert(0, 0, &B);
    assert(db1 != db2);
    db2.insert(0, 0, &B); // B overwrites A.
    assert(db1 == db2); // Both should have B at (0,0).
    db1.insert(1, 1, &C);
    db2.insert(2, 2, &C);
    assert(db1 != db2);
    db1.insert(2, 2, &C);
    db2.insert(1, 1, &C);
    assert(db1 == db2);

    // Inserting data in different orders should make no difference in equality.
    db1.insert(1, 3, &D);
    db1.insert(1, 4, &E);
    db1.insert(1, 5, &F);

    db2.insert(1, 5, &F);
    db2.insert(1, 4, &E);
    db2.insert(1, 3, &D);
    assert(db1 == db2);
    db1.remove(1, 4);
    db2.remove(1, 4);
    assert(db1 == db2);

    // Make them empty again.
    db1.clear();
    db2.clear();
    assert(db1 == db2);
}

int main()
{
    pqt_test_basic();
    pqt_test_insertion();
    pqt_test_remove_root();
    pqt_test_equality();
    assert(get_node_instance_count() == 0);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkOSFile.h"
#include "SkTypes.h"

#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>

#ifdef SK_BUILD_FOR_UNIX
#include <unistd.h>
#endif

#ifdef _WIN32
#include <direct.h>
#include <io.h>
#endif

#ifdef SK_BUILD_FOR_IOS
#import <CoreFoundation/CoreFoundation.h>

static FILE* ios_open_from_bundle(const char path[], const char* perm) {
    // Get a reference to the main bundle
    CFBundleRef mainBundle = CFBundleGetMainBundle();

    // Get a reference to the file's URL
    CFStringRef pathRef = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
    CFURLRef imageURL = CFBundleCopyResourceURL(mainBundle, pathRef, NULL, NULL);
    CFRelease(pathRef);
    if (!imageURL) {
        return nullptr;
    }

    // Convert the URL reference into a string reference
    CFStringRef imagePath = CFURLCopyFileSystemPath(imageURL, kCFURLPOSIXPathStyle);

    // Get the system encoding method
    CFStringEncoding encodingMethod = CFStringGetSystemEncoding();

    // Convert the string reference into a C string
    const char *finalPath = CFStringGetCStringPtr(imagePath, encodingMethod);

    return fopen(finalPath, perm);
}
#endif

FILE* sk_fopen(const char path[], SkFILE_Flags flags) {
    char    perm[4];
    char*   p = perm;

    if (flags & kRead_SkFILE_Flag) {
        *p++ = 'r';
    }
    if (flags & kWrite_SkFILE_Flag) {
        *p++ = 'w';
    }
    *p++ = 'b';
    *p = 0;

    //TODO: on Windows fopen is just ASCII or the current code page,
    //convert to utf16 and use _wfopen
    FILE* file = nullptr;
#ifdef SK_BUILD_FOR_IOS
    // if read-only, try to open from bundle first
    if (kRead_SkFILE_Flag == flags) {
        file = ios_open_from_bundle(path, perm);
    }
    // otherwise just read from the Documents directory (default)
    if (!file) {
#endif
        file = fopen(path, perm);
#ifdef SK_BUILD_FOR_IOS
    }
#endif
    if (nullptr == file && (flags & kWrite_SkFILE_Flag)) {
        SkDEBUGF(("sk_fopen: fopen(\"%s\", \"%s\") returned NULL (errno:%d): %s\n",
                  path, perm, errno, strerror(errno)));
    }
    return file;
}

char* sk_fgets(char* str, int size, FILE* f) {
    return fgets(str, size, (FILE *)f);
}

int sk_feof(FILE *f) {
    // no :: namespace qualifier because it breaks android
    return feof((FILE *)f);
}

size_t sk_fgetsize(FILE* f) {
    SkASSERT(f);

    long curr = ftell(f); // remember where we are
    if (curr < 0) {
        return 0;
    }

    fseek(f, 0, SEEK_END); // go to the end
    long size = ftell(f); // record the size
    if (size < 0) {
        size = 0;
    }

    fseek(f, curr, SEEK_SET); // go back to our prev location
    return size;
}

bool sk_frewind(FILE* f) {
    SkASSERT(f);
    ::rewind(f);
    return true;
}

size_t sk_fread(void* buffer, size_t byteCount, FILE* f) {
    SkASSERT(f);
    if (buffer == nullptr) {
        size_t curr = ftell(f);
        if ((long)curr == -1) {
            SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof(f), ferror(f)));
            return 0;
        }
        int err = fseek(f, (long)byteCount, SEEK_CUR);
        if (err != 0) {
            SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n",
                        byteCount, curr, feof(f), ferror(f), err));
            return 0;
        }
        return byteCount;
    }
    else
        return fread(buffer, 1, byteCount, f);
}

size_t sk_fwrite(const void* buffer, size_t byteCount, FILE* f) {
    SkASSERT(f);
    return fwrite(buffer, 1, byteCount, f);
}

void sk_fflush(FILE* f) {
    SkASSERT(f);
    fflush(f);
}

void sk_fsync(FILE* f) {
#if !defined(_WIN32) && !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__) \
        && !defined(_NEWLIB_VERSION)
    int fd = fileno(f);
    fsync(fd);
#endif
}

bool sk_fseek(FILE* f, size_t byteCount) {
    int err = fseek(f, (long)byteCount, SEEK_SET);
    return err == 0;
}

bool sk_fmove(FILE* f, long byteCount) {
    int err = fseek(f, byteCount, SEEK_CUR);
    return err == 0;
}

size_t sk_ftell(FILE* f) {
    long curr = ftell(f);
    if (curr < 0) {
        return 0;
    }
    return curr;
}

void sk_fclose(FILE* f) {
    SkASSERT(f);
    fclose(f);
}

bool sk_isdir(const char *path) {
    struct stat status;
    if (0 != stat(path, &status)) {
        return false;
    }
    return SkToBool(status.st_mode & S_IFDIR);
}

bool sk_mkdir(const char* path) {
    if (sk_isdir(path)) {
        return true;
    }
    if (sk_exists(path)) {
        fprintf(stderr,
                "sk_mkdir: path '%s' already exists but is not a directory\n",
                path);
        return false;
    }

    int retval;
#ifdef _WIN32
    retval = _mkdir(path);
#else
    retval = mkdir(path, 0777);
#endif
    if (0 == retval) {
        return true;
    } else {
        fprintf(stderr, "sk_mkdir: error %d creating dir '%s'\n", errno, path);
        return false;
    }
}
<commit_msg>Fix memory leaks reported by clang static analyzer.<commit_after>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkOSFile.h"
#include "SkTypes.h"

#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>

#ifdef SK_BUILD_FOR_UNIX
#include <unistd.h>
#endif

#ifdef _WIN32
#include <direct.h>
#include <io.h>
#endif

#ifdef SK_BUILD_FOR_IOS
#import <CoreFoundation/CoreFoundation.h>

static FILE* ios_open_from_bundle(const char path[], const char* perm) {
    // Get a reference to the main bundle
    CFBundleRef mainBundle = CFBundleGetMainBundle();

    // Get a reference to the file's URL
    CFStringRef pathRef = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
    CFURLRef imageURL = CFBundleCopyResourceURL(mainBundle, pathRef, NULL, NULL);
    CFRelease(pathRef);
    if (!imageURL) {
        return nullptr;
    }

    // Convert the URL reference into a string reference
    CFStringRef imagePath = CFURLCopyFileSystemPath(imageURL, kCFURLPOSIXPathStyle);
    CFRelease(imageURL);

    // Get the system encoding method
    CFStringEncoding encodingMethod = CFStringGetSystemEncoding();

    // Convert the string reference into a C string
    const char *finalPath = CFStringGetCStringPtr(imagePath, encodingMethod);
    FILE* fileHandle = fopen(finalPath, perm);
    CFRelease(imagePath);
    return fileHandle;
}
#endif

FILE* sk_fopen(const char path[], SkFILE_Flags flags) {
    char    perm[4];
    char*   p = perm;

    if (flags & kRead_SkFILE_Flag) {
        *p++ = 'r';
    }
    if (flags & kWrite_SkFILE_Flag) {
        *p++ = 'w';
    }
    *p++ = 'b';
    *p = 0;

    //TODO: on Windows fopen is just ASCII or the current code page,
    //convert to utf16 and use _wfopen
    FILE* file = nullptr;
#ifdef SK_BUILD_FOR_IOS
    // if read-only, try to open from bundle first
    if (kRead_SkFILE_Flag == flags) {
        file = ios_open_from_bundle(path, perm);
    }
    // otherwise just read from the Documents directory (default)
    if (!file) {
#endif
        file = fopen(path, perm);
#ifdef SK_BUILD_FOR_IOS
    }
#endif
    if (nullptr == file && (flags & kWrite_SkFILE_Flag)) {
        SkDEBUGF(("sk_fopen: fopen(\"%s\", \"%s\") returned NULL (errno:%d): %s\n",
                  path, perm, errno, strerror(errno)));
    }
    return file;
}

char* sk_fgets(char* str, int size, FILE* f) {
    return fgets(str, size, (FILE *)f);
}

int sk_feof(FILE *f) {
    // no :: namespace qualifier because it breaks android
    return feof((FILE *)f);
}

size_t sk_fgetsize(FILE* f) {
    SkASSERT(f);

    long curr = ftell(f); // remember where we are
    if (curr < 0) {
        return 0;
    }

    fseek(f, 0, SEEK_END); // go to the end
    long size = ftell(f); // record the size
    if (size < 0) {
        size = 0;
    }

    fseek(f, curr, SEEK_SET); // go back to our prev location
    return size;
}

bool sk_frewind(FILE* f) {
    SkASSERT(f);
    ::rewind(f);
    return true;
}

size_t sk_fread(void* buffer, size_t byteCount, FILE* f) {
    SkASSERT(f);
    if (buffer == nullptr) {
        size_t curr = ftell(f);
        if ((long)curr == -1) {
            SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof(f), ferror(f)));
            return 0;
        }
        int err = fseek(f, (long)byteCount, SEEK_CUR);
        if (err != 0) {
            SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n",
                        byteCount, curr, feof(f), ferror(f), err));
            return 0;
        }
        return byteCount;
    }
    else
        return fread(buffer, 1, byteCount, f);
}

size_t sk_fwrite(const void* buffer, size_t byteCount, FILE* f) {
    SkASSERT(f);
    return fwrite(buffer, 1, byteCount, f);
}

void sk_fflush(FILE* f) {
    SkASSERT(f);
    fflush(f);
}

void sk_fsync(FILE* f) {
#if !defined(_WIN32) && !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__) \
        && !defined(_NEWLIB_VERSION)
    int fd = fileno(f);
    fsync(fd);
#endif
}

bool sk_fseek(FILE* f, size_t byteCount) {
    int err = fseek(f, (long)byteCount, SEEK_SET);
    return err == 0;
}

bool sk_fmove(FILE* f, long byteCount) {
    int err = fseek(f, byteCount, SEEK_CUR);
    return err == 0;
}

size_t sk_ftell(FILE* f) {
    long curr = ftell(f);
    if (curr < 0) {
        return 0;
    }
    return curr;
}

void sk_fclose(FILE* f) {
    SkASSERT(f);
    fclose(f);
}

bool sk_isdir(const char *path) {
    struct stat status;
    if (0 != stat(path, &status)) {
        return false;
    }
    return SkToBool(status.st_mode & S_IFDIR);
}

bool sk_mkdir(const char* path) {
    if (sk_isdir(path)) {
        return true;
    }
    if (sk_exists(path)) {
        fprintf(stderr,
                "sk_mkdir: path '%s' already exists but is not a directory\n",
                path);
        return false;
    }

    int retval;
#ifdef _WIN32
    retval = _mkdir(path);
#else
    retval = mkdir(path, 0777);
#endif
    if (0 == retval) {
        return true;
    } else {
        fprintf(stderr, "sk_mkdir: error %d creating dir '%s'\n", errno, path);
        return false;
    }
}
<|endoftext|>


<commit_before>//===-- asan_win.cc -------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Windows-specific details.
//===----------------------------------------------------------------------===//

#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <stdlib.h>

#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"

using namespace __asan;  // NOLINT

extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
int __asan_should_detect_stack_use_after_return() {
  __asan_init();
  return __asan_option_detect_stack_use_after_return;
}

// -------------------- A workaround for the absence of weak symbols ----- {{{
// We don't have a direct equivalent of weak symbols when using MSVC, but we can
// use the /alternatename directive to tell the linker to default a specific
// symbol to a specific value, which works nicely for allocator hooks and
// __asan_default_options().
void __sanitizer_default_malloc_hook(void *ptr, uptr size) { }
void __sanitizer_default_free_hook(void *ptr) { }
const char* __asan_default_default_options() { return ""; }
const char* __asan_default_default_suppressions() { return ""; }
void __asan_default_on_error() {}
// 64-bit msvc will not prepend an underscore for symbols.
#ifdef _WIN64
#pragma comment(linker, "/alternatename:__sanitizer_malloc_hook=__sanitizer_default_malloc_hook")  // NOLINT
#pragma comment(linker, "/alternatename:__sanitizer_free_hook=__sanitizer_default_free_hook")      // NOLINT
#pragma comment(linker, "/alternatename:__asan_default_options=__asan_default_default_options")    // NOLINT
#pragma comment(linker, "/alternatename:__asan_default_suppressions=__asan_default_default_suppressions")    // NOLINT
#pragma comment(linker, "/alternatename:__asan_on_error=__asan_default_on_error")                  // NOLINT
#else
#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook")  // NOLINT
#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook")      // NOLINT
#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options")    // NOLINT
#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions")    // NOLINT
#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error")                  // NOLINT
#endif
// }}}
}  // extern "C"

// ---------------------- Windows-specific interceptors ---------------- {{{
INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
  CHECK(REAL(RtlRaiseException));
  // This is a noreturn function, unless it's one of the exceptions raised to
  // communicate with the debugger, such as the one from OutputDebugString.
  if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
    __asan_handle_no_return();
  REAL(RtlRaiseException)(ExceptionRecord);
}

INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
  CHECK(REAL(RaiseException));
  __asan_handle_no_return();
  REAL(RaiseException)(a, b, c, d);
}

#ifdef _WIN64

INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) {  // NOLINT
  CHECK(REAL(__C_specific_handler));
  __asan_handle_no_return();
  return REAL(__C_specific_handler)(a, b, c, d);
}

#else

INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
  CHECK(REAL(_except_handler3));
  __asan_handle_no_return();
  return REAL(_except_handler3)(a, b, c, d);
}

#if ASAN_DYNAMIC
// This handler is named differently in -MT and -MD CRTs.
#define _except_handler4 _except_handler4_common
#endif
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
  CHECK(REAL(_except_handler4));
  __asan_handle_no_return();
  return REAL(_except_handler4)(a, b, c, d);
}
#endif

static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
  AsanThread *t = (AsanThread*)arg;
  SetCurrentThread(t);
  return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
}

INTERCEPTOR_WINAPI(DWORD, CreateThread,
                   void* security, uptr stack_size,
                   DWORD (__stdcall *start_routine)(void*), void* arg,
                   DWORD thr_flags, void* tid) {
  // Strict init-order checking is thread-hostile.
  if (flags()->strict_init_order)
    StopInitOrderChecking();
  GET_STACK_TRACE_THREAD;
  // FIXME: The CreateThread interceptor is not the same as a pthread_create
  // one.  This is a bandaid fix for PR22025.
  bool detached = false;  // FIXME: how can we determine it on Windows?
  u32 current_tid = GetCurrentTidOrInvalid();
  AsanThread *t =
        AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
  return REAL(CreateThread)(security, stack_size,
                            asan_thread_start, t, thr_flags, tid);
}

// }}}

namespace __asan {

void InitializePlatformInterceptors() {
  ASAN_INTERCEPT_FUNC(CreateThread);

#ifdef _WIN64
  ASAN_INTERCEPT_FUNC(__C_specific_handler);
#else
  ASAN_INTERCEPT_FUNC(_except_handler3);
  ASAN_INTERCEPT_FUNC(_except_handler4);
#endif

  // Try to intercept kernel32!RaiseException, and if that fails, intercept
  // ntdll!RtlRaiseException instead.
  if (!::__interception::OverrideFunction("RaiseException",
                                          (uptr)WRAP(RaiseException),
                                          (uptr *)&REAL(RaiseException))) {
    CHECK(::__interception::OverrideFunction("RtlRaiseException",
                                             (uptr)WRAP(RtlRaiseException),
                                             (uptr *)&REAL(RtlRaiseException)));
  }
}

void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
  UNIMPLEMENTED();
}

// ---------------------- TSD ---------------- {{{
static bool tsd_key_inited = false;

static __declspec(thread) void *fake_tsd = 0;

void AsanTSDInit(void (*destructor)(void *tsd)) {
  // FIXME: we're ignoring the destructor for now.
  tsd_key_inited = true;
}

void *AsanTSDGet() {
  CHECK(tsd_key_inited);
  return fake_tsd;
}

void AsanTSDSet(void *tsd) {
  CHECK(tsd_key_inited);
  fake_tsd = tsd;
}

void PlatformTSDDtor(void *tsd) {
  AsanThread::TSDDtor(tsd);
}
// }}}

// ---------------------- Various stuff ---------------- {{{
void *AsanDoesNotSupportStaticLinkage() {
#if defined(_DEBUG)
#error Please build the runtime with a non-debug CRT: /MD or /MT
#endif
  return 0;
}

void AsanCheckDynamicRTPrereqs() {}

void AsanCheckIncompatibleRT() {}

void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
  UNIMPLEMENTED();
}

void AsanOnDeadlySignal(int, void *siginfo, void *context) {
  UNIMPLEMENTED();
}

#if SANITIZER_WINDOWS64
// Exception handler for dealing with shadow memory.
static LONG CALLBACK
ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
  uptr page_size = GetPageSizeCached();
  // Only handle access violations.
  if (exception_pointers->ExceptionRecord->ExceptionCode !=
      EXCEPTION_ACCESS_VIOLATION) {
    return EXCEPTION_CONTINUE_SEARCH;
  }

  // Only handle access violations that land within the shadow memory.
  uptr addr =
      (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);

  // Check valid shadow range.
  if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH;

  // This is an access violation while trying to read from the shadow. Commit
  // the relevant page and let execution continue.

  // Determine the address of the page that is being accessed.
  uptr page = RoundDownTo(addr, page_size);

  // Query the existing page.
  MEMORY_BASIC_INFORMATION mem_info = {};
  if (::VirtualQuery((LPVOID)page, &mem_info, sizeof(mem_info)) == 0)
    return EXCEPTION_CONTINUE_SEARCH;

  // Commit the page.
  uptr result =
      (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE);
  if (result != page) return EXCEPTION_CONTINUE_SEARCH;

  // The page mapping succeeded, so continue execution as usual.
  return EXCEPTION_CONTINUE_EXECUTION;
}

#endif

void InitializePlatformExceptionHandlers() {
#if SANITIZER_WINDOWS64
  // On Win64, we map memory on demand with access violation handler.
  // Install our exception handler.
  CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler));
#endif
}

static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;

// Check based on flags if we should report this exception.
static bool ShouldReportDeadlyException(unsigned code) {
  switch (code) {
    case EXCEPTION_ACCESS_VIOLATION:
    case EXCEPTION_IN_PAGE_ERROR:
      return common_flags()->handle_segv;
    case EXCEPTION_BREAKPOINT:
    case EXCEPTION_ILLEGAL_INSTRUCTION: {
      return common_flags()->handle_sigill;
    }
  }
  return false;
}

// Return the textual name for this exception.
const char *DescribeSignalOrException(int signo) {
  unsigned code = signo;
  // Get the string description of the exception if this is a known deadly
  // exception.
  switch (code) {
    case EXCEPTION_ACCESS_VIOLATION:
      return "access-violation";
    case EXCEPTION_IN_PAGE_ERROR:
      return "in-page-error";
    case EXCEPTION_BREAKPOINT:
      return "breakpoint";
    case EXCEPTION_ILLEGAL_INSTRUCTION:
      return "illegal-instruction";
  }
  return nullptr;
}

static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
  EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
  CONTEXT *context = info->ContextRecord;

  if (ShouldReportDeadlyException(exception_record->ExceptionCode)) {
    SignalContext sig = SignalContext::Create(exception_record, context);
    ReportDeadlySignal(exception_record->ExceptionCode, sig);
  }

  // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.

  return default_seh_handler(info);
}

// We want to install our own exception handler (EH) to print helpful reports
// on access violations and whatnot.  Unfortunately, the CRT initializers assume
// they are run before any user code and drop any previously-installed EHs on
// the floor, so we can't install our handler inside __asan_init.
// (See crt0dat.c in the CRT sources for the details)
//
// Things get even more complicated with the dynamic runtime, as it finishes its
// initialization before the .exe module CRT begins to initialize.
//
// For the static runtime (-MT), it's enough to put a callback to
// __asan_set_seh_filter in the last section for C initializers.
//
// For the dynamic runtime (-MD), we want link the same
// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
// will be called for each instrumented module.  This ensures that at least one
// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
int __asan_set_seh_filter() {
  // We should only store the previous handler if it's not our own handler in
  // order to avoid loops in the EH chain.
  auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
  if (prev_seh_handler != &SEHHandler)
    default_seh_handler = prev_seh_handler;
  return 0;
}

#if !ASAN_DYNAMIC
// The CRT runs initializers in this order:
// - C initializers, from XIA to XIZ
// - C++ initializers, from XCA to XCZ
// Prior to 2015, the CRT set the unhandled exception filter at priority XIY,
// near the end of C initialization. Starting in 2015, it was moved to the
// beginning of C++ initialization. We set our priority to XCAB to run
// immediately after the CRT runs. This way, our exception filter is called
// first and we can delegate to their filter if appropriate.
#pragma section(".CRT$XCAB", long, read)  // NOLINT
__declspec(allocate(".CRT$XCAB"))
    int (*__intercept_seh)() = __asan_set_seh_filter;
#endif
// }}}
}  // namespace __asan

#endif  // _WIN32
<commit_msg>Fix the Windows build after r281546<commit_after>//===-- asan_win.cc -------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Windows-specific details.
//===----------------------------------------------------------------------===//

#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <stdlib.h>

#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"

using namespace __asan;  // NOLINT

extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
int __asan_should_detect_stack_use_after_return() {
  __asan_init();
  return __asan_option_detect_stack_use_after_return;
}

// -------------------- A workaround for the absence of weak symbols ----- {{{
// We don't have a direct equivalent of weak symbols when using MSVC, but we can
// use the /alternatename directive to tell the linker to default a specific
// symbol to a specific value, which works nicely for allocator hooks and
// __asan_default_options().
void __sanitizer_default_malloc_hook(void *ptr, uptr size) { }
void __sanitizer_default_free_hook(void *ptr) { }
void __sanitizer_default_print_memory_profile(int top_percent) {}
const char* __asan_default_default_options() { return ""; }
const char* __asan_default_default_suppressions() { return ""; }
void __asan_default_on_error() {}
// 64-bit msvc will not prepend an underscore for symbols.
#ifdef _WIN64
#pragma comment(linker, "/alternatename:__sanitizer_malloc_hook=__sanitizer_default_malloc_hook")  // NOLINT
#pragma comment(linker, "/alternatename:__sanitizer_free_hook=__sanitizer_default_free_hook")      // NOLINT
#pragma comment(linker, "/alternatename:__sanitizer_print_memory_profile=__sanitizer_default_print_memory_profile") // NOLINT
#pragma comment(linker, "/alternatename:__asan_default_options=__asan_default_default_options")    // NOLINT
#pragma comment(linker, "/alternatename:__asan_default_suppressions=__asan_default_default_suppressions")    // NOLINT
#pragma comment(linker, "/alternatename:__asan_on_error=__asan_default_on_error")                  // NOLINT
#else
#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook")  // NOLINT
#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook")      // NOLINT
#pragma comment(linker, "/alternatename:___sanitizer_print_memory_profile=___sanitizer_default_print_memory_profile") // NOLINT
#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options")    // NOLINT
#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions")    // NOLINT
#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error")                  // NOLINT
#endif
// }}}
}  // extern "C"

// ---------------------- Windows-specific interceptors ---------------- {{{
INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
  CHECK(REAL(RtlRaiseException));
  // This is a noreturn function, unless it's one of the exceptions raised to
  // communicate with the debugger, such as the one from OutputDebugString.
  if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
    __asan_handle_no_return();
  REAL(RtlRaiseException)(ExceptionRecord);
}

INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
  CHECK(REAL(RaiseException));
  __asan_handle_no_return();
  REAL(RaiseException)(a, b, c, d);
}

#ifdef _WIN64

INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) {  // NOLINT
  CHECK(REAL(__C_specific_handler));
  __asan_handle_no_return();
  return REAL(__C_specific_handler)(a, b, c, d);
}

#else

INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
  CHECK(REAL(_except_handler3));
  __asan_handle_no_return();
  return REAL(_except_handler3)(a, b, c, d);
}

#if ASAN_DYNAMIC
// This handler is named differently in -MT and -MD CRTs.
#define _except_handler4 _except_handler4_common
#endif
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
  CHECK(REAL(_except_handler4));
  __asan_handle_no_return();
  return REAL(_except_handler4)(a, b, c, d);
}
#endif

static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
  AsanThread *t = (AsanThread*)arg;
  SetCurrentThread(t);
  return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
}

INTERCEPTOR_WINAPI(DWORD, CreateThread,
                   void* security, uptr stack_size,
                   DWORD (__stdcall *start_routine)(void*), void* arg,
                   DWORD thr_flags, void* tid) {
  // Strict init-order checking is thread-hostile.
  if (flags()->strict_init_order)
    StopInitOrderChecking();
  GET_STACK_TRACE_THREAD;
  // FIXME: The CreateThread interceptor is not the same as a pthread_create
  // one.  This is a bandaid fix for PR22025.
  bool detached = false;  // FIXME: how can we determine it on Windows?
  u32 current_tid = GetCurrentTidOrInvalid();
  AsanThread *t =
        AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
  return REAL(CreateThread)(security, stack_size,
                            asan_thread_start, t, thr_flags, tid);
}

// }}}

namespace __asan {

void InitializePlatformInterceptors() {
  ASAN_INTERCEPT_FUNC(CreateThread);

#ifdef _WIN64
  ASAN_INTERCEPT_FUNC(__C_specific_handler);
#else
  ASAN_INTERCEPT_FUNC(_except_handler3);
  ASAN_INTERCEPT_FUNC(_except_handler4);
#endif

  // Try to intercept kernel32!RaiseException, and if that fails, intercept
  // ntdll!RtlRaiseException instead.
  if (!::__interception::OverrideFunction("RaiseException",
                                          (uptr)WRAP(RaiseException),
                                          (uptr *)&REAL(RaiseException))) {
    CHECK(::__interception::OverrideFunction("RtlRaiseException",
                                             (uptr)WRAP(RtlRaiseException),
                                             (uptr *)&REAL(RtlRaiseException)));
  }
}

void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
  UNIMPLEMENTED();
}

// ---------------------- TSD ---------------- {{{
static bool tsd_key_inited = false;

static __declspec(thread) void *fake_tsd = 0;

void AsanTSDInit(void (*destructor)(void *tsd)) {
  // FIXME: we're ignoring the destructor for now.
  tsd_key_inited = true;
}

void *AsanTSDGet() {
  CHECK(tsd_key_inited);
  return fake_tsd;
}

void AsanTSDSet(void *tsd) {
  CHECK(tsd_key_inited);
  fake_tsd = tsd;
}

void PlatformTSDDtor(void *tsd) {
  AsanThread::TSDDtor(tsd);
}
// }}}

// ---------------------- Various stuff ---------------- {{{
void *AsanDoesNotSupportStaticLinkage() {
#if defined(_DEBUG)
#error Please build the runtime with a non-debug CRT: /MD or /MT
#endif
  return 0;
}

void AsanCheckDynamicRTPrereqs() {}

void AsanCheckIncompatibleRT() {}

void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
  UNIMPLEMENTED();
}

void AsanOnDeadlySignal(int, void *siginfo, void *context) {
  UNIMPLEMENTED();
}

#if SANITIZER_WINDOWS64
// Exception handler for dealing with shadow memory.
static LONG CALLBACK
ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
  uptr page_size = GetPageSizeCached();
  // Only handle access violations.
  if (exception_pointers->ExceptionRecord->ExceptionCode !=
      EXCEPTION_ACCESS_VIOLATION) {
    return EXCEPTION_CONTINUE_SEARCH;
  }

  // Only handle access violations that land within the shadow memory.
  uptr addr =
      (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);

  // Check valid shadow range.
  if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH;

  // This is an access violation while trying to read from the shadow. Commit
  // the relevant page and let execution continue.

  // Determine the address of the page that is being accessed.
  uptr page = RoundDownTo(addr, page_size);

  // Query the existing page.
  MEMORY_BASIC_INFORMATION mem_info = {};
  if (::VirtualQuery((LPVOID)page, &mem_info, sizeof(mem_info)) == 0)
    return EXCEPTION_CONTINUE_SEARCH;

  // Commit the page.
  uptr result =
      (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE);
  if (result != page) return EXCEPTION_CONTINUE_SEARCH;

  // The page mapping succeeded, so continue execution as usual.
  return EXCEPTION_CONTINUE_EXECUTION;
}

#endif

void InitializePlatformExceptionHandlers() {
#if SANITIZER_WINDOWS64
  // On Win64, we map memory on demand with access violation handler.
  // Install our exception handler.
  CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler));
#endif
}

static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;

// Check based on flags if we should report this exception.
static bool ShouldReportDeadlyException(unsigned code) {
  switch (code) {
    case EXCEPTION_ACCESS_VIOLATION:
    case EXCEPTION_IN_PAGE_ERROR:
      return common_flags()->handle_segv;
    case EXCEPTION_BREAKPOINT:
    case EXCEPTION_ILLEGAL_INSTRUCTION: {
      return common_flags()->handle_sigill;
    }
  }
  return false;
}

// Return the textual name for this exception.
const char *DescribeSignalOrException(int signo) {
  unsigned code = signo;
  // Get the string description of the exception if this is a known deadly
  // exception.
  switch (code) {
    case EXCEPTION_ACCESS_VIOLATION:
      return "access-violation";
    case EXCEPTION_IN_PAGE_ERROR:
      return "in-page-error";
    case EXCEPTION_BREAKPOINT:
      return "breakpoint";
    case EXCEPTION_ILLEGAL_INSTRUCTION:
      return "illegal-instruction";
  }
  return nullptr;
}

static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
  EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
  CONTEXT *context = info->ContextRecord;

  if (ShouldReportDeadlyException(exception_record->ExceptionCode)) {
    SignalContext sig = SignalContext::Create(exception_record, context);
    ReportDeadlySignal(exception_record->ExceptionCode, sig);
  }

  // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.

  return default_seh_handler(info);
}

// We want to install our own exception handler (EH) to print helpful reports
// on access violations and whatnot.  Unfortunately, the CRT initializers assume
// they are run before any user code and drop any previously-installed EHs on
// the floor, so we can't install our handler inside __asan_init.
// (See crt0dat.c in the CRT sources for the details)
//
// Things get even more complicated with the dynamic runtime, as it finishes its
// initialization before the .exe module CRT begins to initialize.
//
// For the static runtime (-MT), it's enough to put a callback to
// __asan_set_seh_filter in the last section for C initializers.
//
// For the dynamic runtime (-MD), we want link the same
// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
// will be called for each instrumented module.  This ensures that at least one
// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
int __asan_set_seh_filter() {
  // We should only store the previous handler if it's not our own handler in
  // order to avoid loops in the EH chain.
  auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
  if (prev_seh_handler != &SEHHandler)
    default_seh_handler = prev_seh_handler;
  return 0;
}

#if !ASAN_DYNAMIC
// The CRT runs initializers in this order:
// - C initializers, from XIA to XIZ
// - C++ initializers, from XCA to XCZ
// Prior to 2015, the CRT set the unhandled exception filter at priority XIY,
// near the end of C initialization. Starting in 2015, it was moved to the
// beginning of C++ initialization. We set our priority to XCAB to run
// immediately after the CRT runs. This way, our exception filter is called
// first and we can delegate to their filter if appropriate.
#pragma section(".CRT$XCAB", long, read)  // NOLINT
__declspec(allocate(".CRT$XCAB"))
    int (*__intercept_seh)() = __asan_set_seh_filter;
#endif
// }}}
}  // namespace __asan

#endif  // _WIN32
<|endoftext|>


<commit_before>/*
* (C) 2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "fuzzers.h"
#include <botan/internal/mem_pool.h>
#include <botan/internal/bit_ops.h>
#include <vector>
#include <map>
#include <utility>

#include <stdlib.h>

namespace {

size_t compute_expected_alignment(size_t plen)
   {
   if(Botan::is_power_of_2(plen))
      {
      return plen;
      }
   else
      {
      return 8;
      }
   }

struct RawPage
   {
   public:
      RawPage(void* p) : m_p(p) {}
      ~RawPage() { std::free(m_p); }

      RawPage(const RawPage& other) = default;
      RawPage& operator=(const RawPage& other) = default;

      RawPage(RawPage&& other) : m_p(nullptr)
         {
         std::swap(m_p, other.m_p);
         }

      RawPage& operator=(RawPage&& other)
         {
         if(this != &other)
            {
            std::swap(m_p, other.m_p);
            }
         return (*this);
         }

      void* ptr() const { return m_p; }
   private:
      void* m_p;
   };

std::vector<RawPage> allocate_raw_pages(size_t count, size_t page_size)
   {
   std::vector<RawPage> pages;
   pages.reserve(count);

   for(size_t i = 0; i != count; ++i)
      {
      void* ptr = nullptr;

      ::posix_memalign(&ptr, page_size, page_size);

      if(ptr)
         {
         fprintf(stderr, "%p\n", ptr);
         pages.push_back(RawPage(ptr));
         }
      }

   return pages;
   }

}

void fuzz(const uint8_t in[], size_t in_len)
   {
   const size_t page_count = 4;
   const size_t page_size = 4096;

   // static to avoid repeated allocations
   static std::vector<RawPage> raw_mem = allocate_raw_pages(page_count, page_size);

   std::vector<void*> mem_pages;
   mem_pages.reserve(raw_mem.size());
   for(size_t i = 0; i != raw_mem.size(); ++i)
      mem_pages.push_back(raw_mem[i].ptr());

   Botan::Memory_Pool pool(mem_pages, page_size);
   std::map<uint8_t*, size_t> ptrs;

   while(in_len > 0)
      {
      const uint8_t op = in[0] % 2;
      size_t idx = (in[0] >> 1);
      in += 1;
      in_len -= 1;

      if(in_len > 0 && idx < 4)
         {
         idx = idx * 256 + in[0];
         in += 1;
         in_len -= 1;
         }

      //printf("%d %d\n", op, idx);

      if(op == 0)
         {
         const size_t plen = idx + 1; // ensure non-zero
         uint8_t* p = static_cast<uint8_t*>(pool.allocate(plen));

         if(p)
            {
            const size_t expected_alignment = compute_expected_alignment(plen);
            const size_t alignment = reinterpret_cast<uintptr_t>(p) % expected_alignment;
            if(alignment != 0)
               {
               FUZZER_WRITE_AND_CRASH("Pointer allocated non-aligned pointer " << static_cast<void*>(p) << " for len " << plen
                                      << " expected " << expected_alignment << " got " << alignment);
               }

            //printf("alloc %d -> %p\n", plen, p);

            for(size_t i = 0; i != plen; ++i)
               {
               if(p[i] != 0)
                  {
                  FUZZER_WRITE_AND_CRASH("Pool gave out non-zeroed memory");
                  }
               }

            // verify it becomes zeroed later
            std::memset(p, idx, plen);

            auto insert = ptrs.insert(std::make_pair(p, plen));
            if(insert.second == false)
               {
               FUZZER_WRITE_AND_CRASH("Pointer " << static_cast<void*>(p) << " already existed\n");
               }

            auto itr = insert.first;

            // Verify this pointer doesn't overlap with the one before it
            if(itr != ptrs.begin())
               {
               auto before = std::prev(itr);
               auto ptr_before = *before;

               if(ptr_before.first + ptr_before.second > p)
                  {
                  FUZZER_WRITE_AND_CRASH("Previous " << static_cast<void*>(ptr_before.first) << "/" << ptr_before.second <<
                                         " overlaps with new " << static_cast<void*>(p));
                  }
               }

            auto after = std::next(itr);

            if(after != ptrs.end())
               {
               if(p + plen > after->first)
                  {
                  FUZZER_WRITE_AND_CRASH("New " << static_cast<void*>(p) << "/" << plen
                                         << " overlaps following " << static_cast<void*>(after->first));
                  }
               }
            }
         }
      else if(op == 1)
         {
         if(ptrs.empty())
            return;

         size_t which_ptr = idx % ptrs.size();

         auto itr = ptrs.begin();

         while(which_ptr-- > 0)
            {
            ++itr;
            }

         //printf("free %p %d\n", itr->first, itr->second);
         FUZZER_ASSERT_TRUE(pool.deallocate(itr->first, itr->second));
         ptrs.erase(itr);
         }
      }
   }
<commit_msg>In fuzzer remove debug print and check return value of posix_memalign<commit_after>/*
* (C) 2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "fuzzers.h"
#include <botan/internal/mem_pool.h>
#include <botan/internal/bit_ops.h>
#include <vector>
#include <map>
#include <utility>

#include <stdlib.h>

namespace {

size_t compute_expected_alignment(size_t plen)
   {
   if(Botan::is_power_of_2(plen))
      {
      return plen;
      }
   else
      {
      return 8;
      }
   }

struct RawPage
   {
   public:
      RawPage(void* p) : m_p(p) {}
      ~RawPage() { std::free(m_p); }

      RawPage(const RawPage& other) = default;
      RawPage& operator=(const RawPage& other) = default;

      RawPage(RawPage&& other) : m_p(nullptr)
         {
         std::swap(m_p, other.m_p);
         }

      RawPage& operator=(RawPage&& other)
         {
         if(this != &other)
            {
            std::swap(m_p, other.m_p);
            }
         return (*this);
         }

      void* ptr() const { return m_p; }
   private:
      void* m_p;
   };

std::vector<RawPage> allocate_raw_pages(size_t count, size_t page_size)
   {
   std::vector<RawPage> pages;
   pages.reserve(count);

   for(size_t i = 0; i != count; ++i)
      {
      void* ptr = nullptr;

      int rc = ::posix_memalign(&ptr, page_size, page_size);
      FUZZER_ASSERT_EQUAL(rc, 0);

      if(ptr)
         {
         pages.push_back(RawPage(ptr));
         }
      }

   return pages;
   }

}

void fuzz(const uint8_t in[], size_t in_len)
   {
   const size_t page_count = 4;
   const size_t page_size = 4096;

   // static to avoid repeated allocations
   static std::vector<RawPage> raw_mem = allocate_raw_pages(page_count, page_size);

   std::vector<void*> mem_pages;
   mem_pages.reserve(raw_mem.size());
   for(size_t i = 0; i != raw_mem.size(); ++i)
      mem_pages.push_back(raw_mem[i].ptr());

   Botan::Memory_Pool pool(mem_pages, page_size);
   std::map<uint8_t*, size_t> ptrs;

   while(in_len > 0)
      {
      const uint8_t op = in[0] % 2;
      size_t idx = (in[0] >> 1);
      in += 1;
      in_len -= 1;

      if(in_len > 0 && idx < 4)
         {
         idx = idx * 256 + in[0];
         in += 1;
         in_len -= 1;
         }

      //printf("%d %d\n", op, idx);

      if(op == 0)
         {
         const size_t plen = idx + 1; // ensure non-zero
         uint8_t* p = static_cast<uint8_t*>(pool.allocate(plen));

         if(p)
            {
            const size_t expected_alignment = compute_expected_alignment(plen);
            const size_t alignment = reinterpret_cast<uintptr_t>(p) % expected_alignment;
            if(alignment != 0)
               {
               FUZZER_WRITE_AND_CRASH("Pointer allocated non-aligned pointer " << static_cast<void*>(p) << " for len " << plen
                                      << " expected " << expected_alignment << " got " << alignment);
               }

            //printf("alloc %d -> %p\n", plen, p);

            for(size_t i = 0; i != plen; ++i)
               {
               if(p[i] != 0)
                  {
                  FUZZER_WRITE_AND_CRASH("Pool gave out non-zeroed memory");
                  }
               }

            // verify it becomes zeroed later
            std::memset(p, idx, plen);

            auto insert = ptrs.insert(std::make_pair(p, plen));
            if(insert.second == false)
               {
               FUZZER_WRITE_AND_CRASH("Pointer " << static_cast<void*>(p) << " already existed\n");
               }

            auto itr = insert.first;

            // Verify this pointer doesn't overlap with the one before it
            if(itr != ptrs.begin())
               {
               auto before = std::prev(itr);
               auto ptr_before = *before;

               if(ptr_before.first + ptr_before.second > p)
                  {
                  FUZZER_WRITE_AND_CRASH("Previous " << static_cast<void*>(ptr_before.first) << "/" << ptr_before.second <<
                                         " overlaps with new " << static_cast<void*>(p));
                  }
               }

            auto after = std::next(itr);

            if(after != ptrs.end())
               {
               if(p + plen > after->first)
                  {
                  FUZZER_WRITE_AND_CRASH("New " << static_cast<void*>(p) << "/" << plen
                                         << " overlaps following " << static_cast<void*>(after->first));
                  }
               }
            }
         }
      else if(op == 1)
         {
         if(ptrs.empty())
            return;

         size_t which_ptr = idx % ptrs.size();

         auto itr = ptrs.begin();

         while(which_ptr-- > 0)
            {
            ++itr;
            }

         //printf("free %p %d\n", itr->first, itr->second);
         FUZZER_ASSERT_TRUE(pool.deallocate(itr->first, itr->second));
         ptrs.erase(itr);
         }
      }
   }
<|endoftext|>