<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/boot_times_loader.h"

#include <vector>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/common/chrome_switches.h"

namespace {

typedef struct Stats {
  std::string uptime;
  std::string disk;

  Stats() : uptime(std::string()), disk(std::string()) {}
};

}

namespace chromeos {

// File uptime logs are located in.
static const char kLogPath[] = "/tmp";
// Prefix for the time measurement files.
static const char kUptimePrefix[] = "uptime-";
// Prefix for the disk usage files.
static const char kDiskPrefix[] = "disk-";
// Name of the time that Chrome's main() is called.
static const char kChromeMain[] = "chrome-main";
// Delay in milliseconds between file read attempts.
static const int64 kReadAttemptDelayMs = 250;

BootTimesLoader::BootTimesLoader() : backend_(new Backend()) {
}

BootTimesLoader::Handle BootTimesLoader::GetBootTimes(
    CancelableRequestConsumerBase* consumer,
    BootTimesLoader::GetBootTimesCallback* callback) {
  if (!g_browser_process->file_thread()) {
    // This should only happen if Chrome is shutting down, so we don't do
    // anything.
    return 0;
  }

  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(switches::kTestType)) {
    // TODO(davemoore) This avoids boottimes for tests. This needs to be
    // replaced with a mock of BootTimesLoader.
    return 0;
  }

  scoped_refptr<CancelableRequest<GetBootTimesCallback> > request(
      new CancelableRequest<GetBootTimesCallback>(callback));
  AddRequest(request, consumer);

  ChromeThread::PostTask(
      ChromeThread::FILE,
      FROM_HERE,
      NewRunnableMethod(backend_.get(), &Backend::GetBootTimes, request));
  return request->handle();
}

// Extracts the uptime value from files located in /tmp, returning the
// value as a double in value.
static bool GetTime(const std::string& log, double* value) {
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(log);
  std::string contents;
  *value = 0.0;
  if (file_util::ReadFileToString(log_file, &contents)) {
    size_t space_index = contents.find(' ');
    size_t chars_left =
        space_index != std::string::npos ? space_index : std::string::npos;
    std::string value_string = contents.substr(0, chars_left);
    return StringToDouble(value_string, value);
  }
  return false;
}

void BootTimesLoader::Backend::GetBootTimes(
    scoped_refptr<GetBootTimesRequest> request) {
  const char* kFirmwareBootTime = "firmware-boot-time";
  const char* kPreStartup = "pre-startup";
  const char* kChromeExec = "chrome-exec";
  const char* kChromeMain = "chrome-main";
  const char* kXStarted = "x-started";
  const char* kLoginPromptReady = "login-prompt-ready";
  std::string uptime_prefix = kUptimePrefix;

  if (request->canceled())
    return;

  // Wait until login_prompt_ready is output by reposting.
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(kLoginPromptReady);
  if (!file_util::PathExists(log_file)) {
    ChromeThread::PostDelayedTask(
        ChromeThread::FILE,
        FROM_HERE,
        NewRunnableMethod(this, &Backend::GetBootTimes, request),
        kReadAttemptDelayMs);
    return;
  }

  BootTimes boot_times;

  GetTime(kFirmwareBootTime, &boot_times.firmware);
  GetTime(uptime_prefix + kPreStartup, &boot_times.pre_startup);
  GetTime(uptime_prefix + kXStarted, &boot_times.x_started);
  GetTime(uptime_prefix + kChromeExec, &boot_times.chrome_exec);
  GetTime(uptime_prefix + kChromeMain, &boot_times.chrome_main);
  GetTime(uptime_prefix + kLoginPromptReady, &boot_times.login_prompt_ready);

  request->ForwardResult(
      GetBootTimesCallback::TupleType(request->handle(), boot_times));
}

static void RecordStatsDelayed(
    const std::string& name,
    const std::string& uptime,
    const std::string& disk) {
  const FilePath log_path(kLogPath);
  std::string disk_prefix = kDiskPrefix;
  const FilePath uptime_output =
      log_path.Append(FilePath(kUptimePrefix + name));
  const FilePath disk_output = log_path.Append(FilePath(kDiskPrefix + name));

  // Write out the files, ensuring that they don't exist already.
  if (!file_util::PathExists(uptime_output))
    file_util::WriteFile(uptime_output, uptime.data(), uptime.size());
  if (!file_util::PathExists(disk_output))
    file_util::WriteFile(disk_output, disk.data(), disk.size());
}

static void RecordStats(
    const std::string& name, const Stats& stats) {
  ChromeThread::PostTask(
      ChromeThread::FILE, FROM_HERE,
      NewRunnableFunction(RecordStatsDelayed, name, stats.uptime, stats.disk));
}

static Stats GetCurrentStats() {
  const FilePath kProcUptime("/proc/uptime");
  const FilePath kDiskStat("/sys/block/sda/stat");
  Stats stats;

  file_util::ReadFileToString(kProcUptime, &stats.uptime);
  file_util::ReadFileToString(kDiskStat, &stats.disk);
  return stats;
}

// static
void BootTimesLoader::RecordCurrentStats(const std::string& name) {
  Stats stats = GetCurrentStats();
  RecordStats(name, stats);
}

// Used to hold the stats at main().
static Stats chrome_main_stats_;

void BootTimesLoader::SaveChromeMainStats() {
  chrome_main_stats_ = GetCurrentStats();
}

void BootTimesLoader::RecordChromeMainStats() {
  RecordStats(kChromeMain, chrome_main_stats_);
}

}  // namespace chromeos
<commit_msg>Fix new gcc error. This only happens on the ARM build, using a different compiler.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/boot_times_loader.h"

#include <vector>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/common/chrome_switches.h"

namespace {

typedef struct Stats {
  std::string uptime;
  std::string disk;

  Stats() : uptime(std::string()), disk(std::string()) {}
} Stats;

}

namespace chromeos {

// File uptime logs are located in.
static const char kLogPath[] = "/tmp";
// Prefix for the time measurement files.
static const char kUptimePrefix[] = "uptime-";
// Prefix for the disk usage files.
static const char kDiskPrefix[] = "disk-";
// Name of the time that Chrome's main() is called.
static const char kChromeMain[] = "chrome-main";
// Delay in milliseconds between file read attempts.
static const int64 kReadAttemptDelayMs = 250;

BootTimesLoader::BootTimesLoader() : backend_(new Backend()) {
}

BootTimesLoader::Handle BootTimesLoader::GetBootTimes(
    CancelableRequestConsumerBase* consumer,
    BootTimesLoader::GetBootTimesCallback* callback) {
  if (!g_browser_process->file_thread()) {
    // This should only happen if Chrome is shutting down, so we don't do
    // anything.
    return 0;
  }

  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(switches::kTestType)) {
    // TODO(davemoore) This avoids boottimes for tests. This needs to be
    // replaced with a mock of BootTimesLoader.
    return 0;
  }

  scoped_refptr<CancelableRequest<GetBootTimesCallback> > request(
      new CancelableRequest<GetBootTimesCallback>(callback));
  AddRequest(request, consumer);

  ChromeThread::PostTask(
      ChromeThread::FILE,
      FROM_HERE,
      NewRunnableMethod(backend_.get(), &Backend::GetBootTimes, request));
  return request->handle();
}

// Extracts the uptime value from files located in /tmp, returning the
// value as a double in value.
static bool GetTime(const std::string& log, double* value) {
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(log);
  std::string contents;
  *value = 0.0;
  if (file_util::ReadFileToString(log_file, &contents)) {
    size_t space_index = contents.find(' ');
    size_t chars_left =
        space_index != std::string::npos ? space_index : std::string::npos;
    std::string value_string = contents.substr(0, chars_left);
    return StringToDouble(value_string, value);
  }
  return false;
}

void BootTimesLoader::Backend::GetBootTimes(
    scoped_refptr<GetBootTimesRequest> request) {
  const char* kFirmwareBootTime = "firmware-boot-time";
  const char* kPreStartup = "pre-startup";
  const char* kChromeExec = "chrome-exec";
  const char* kChromeMain = "chrome-main";
  const char* kXStarted = "x-started";
  const char* kLoginPromptReady = "login-prompt-ready";
  std::string uptime_prefix = kUptimePrefix;

  if (request->canceled())
    return;

  // Wait until login_prompt_ready is output by reposting.
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(kLoginPromptReady);
  if (!file_util::PathExists(log_file)) {
    ChromeThread::PostDelayedTask(
        ChromeThread::FILE,
        FROM_HERE,
        NewRunnableMethod(this, &Backend::GetBootTimes, request),
        kReadAttemptDelayMs);
    return;
  }

  BootTimes boot_times;

  GetTime(kFirmwareBootTime, &boot_times.firmware);
  GetTime(uptime_prefix + kPreStartup, &boot_times.pre_startup);
  GetTime(uptime_prefix + kXStarted, &boot_times.x_started);
  GetTime(uptime_prefix + kChromeExec, &boot_times.chrome_exec);
  GetTime(uptime_prefix + kChromeMain, &boot_times.chrome_main);
  GetTime(uptime_prefix + kLoginPromptReady, &boot_times.login_prompt_ready);

  request->ForwardResult(
      GetBootTimesCallback::TupleType(request->handle(), boot_times));
}

static void RecordStatsDelayed(
    const std::string& name,
    const std::string& uptime,
    const std::string& disk) {
  const FilePath log_path(kLogPath);
  std::string disk_prefix = kDiskPrefix;
  const FilePath uptime_output =
      log_path.Append(FilePath(kUptimePrefix + name));
  const FilePath disk_output = log_path.Append(FilePath(kDiskPrefix + name));

  // Write out the files, ensuring that they don't exist already.
  if (!file_util::PathExists(uptime_output))
    file_util::WriteFile(uptime_output, uptime.data(), uptime.size());
  if (!file_util::PathExists(disk_output))
    file_util::WriteFile(disk_output, disk.data(), disk.size());
}

static void RecordStats(
    const std::string& name, const Stats& stats) {
  ChromeThread::PostTask(
      ChromeThread::FILE, FROM_HERE,
      NewRunnableFunction(RecordStatsDelayed, name, stats.uptime, stats.disk));
}

static Stats GetCurrentStats() {
  const FilePath kProcUptime("/proc/uptime");
  const FilePath kDiskStat("/sys/block/sda/stat");
  Stats stats;

  file_util::ReadFileToString(kProcUptime, &stats.uptime);
  file_util::ReadFileToString(kDiskStat, &stats.disk);
  return stats;
}

// static
void BootTimesLoader::RecordCurrentStats(const std::string& name) {
  Stats stats = GetCurrentStats();
  RecordStats(name, stats);
}

// Used to hold the stats at main().
static Stats chrome_main_stats_;

void BootTimesLoader::SaveChromeMainStats() {
  chrome_main_stats_ = GetCurrentStats();
}

void BootTimesLoader::RecordChromeMainStats() {
  RecordStats(kChromeMain, chrome_main_stats_);
}

}  // namespace chromeos
<|endoftext|>


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

#include "chrome/browser/ui/views/find_bar_host.h"

#include "chrome/browser/ui/find_bar/find_bar_controller.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/tab_contents/tab_contents_view.h"

void FindBarHost::AudibleAlert() {
  // TODO(davemoore) implement.
  NOTIMPLEMENTED();
}

void FindBarHost::GetWidgetPositionNative(gfx::Rect* avoid_overlapping_rect) {
  gfx::Rect frame_rect = host()->GetTopLevelWidget()->GetWindowScreenBounds();
  TabContentsView* tab_view = find_bar_controller_->tab_contents()->view();
  gfx::Rect webcontents_rect;
  tab_view->GetViewBounds(&webcontents_rect);
  avoid_overlapping_rect->Offset(0, webcontents_rect.y() - frame_rect.y());
}

bool FindBarHost::ShouldForwardKeyEventToWebpageNative(
    const views::KeyEvent& key_event) {
  return true;
}
<commit_msg>Fix overlapping_rect's screen coordinate calculation in find bar<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.

#include "chrome/browser/ui/views/find_bar_host.h"

#include "chrome/browser/ui/find_bar/find_bar_controller.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/tab_contents/tab_contents_view.h"

#if defined(TOUCH_UI)
#include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
#endif

void FindBarHost::AudibleAlert() {
  // TODO(davemoore) implement.
  NOTIMPLEMENTED();
}

void FindBarHost::GetWidgetPositionNative(gfx::Rect* avoid_overlapping_rect) {
  gfx::Rect frame_rect = host()->GetTopLevelWidget()->GetWindowScreenBounds();
  TabContentsView* tab_view = find_bar_controller_->tab_contents()->view();
  gfx::Rect webcontents_rect;
#if defined(TOUCH_UI)
  webcontents_rect =
      static_cast<TabContentsViewTouch*>(tab_view)->GetScreenBounds();
#else
  tab_view->GetViewBounds(&webcontents_rect);
#endif
  avoid_overlapping_rect->Offset(0, webcontents_rect.y() - frame_rect.y());
}

bool FindBarHost::ShouldForwardKeyEventToWebpageNative(
    const views::KeyEvent& key_event) {
  return true;
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <windows.h>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/logging_win.h"
#include "base/process/process.h"
#include "base/synchronization/waitable_event.h"
#include "base/template_util.h"
#include "components/browser_watcher/exit_code_watcher_win.h"
#include "components/browser_watcher/exit_funnel_win.h"
#include "components/browser_watcher/watcher_main_api_win.h"

namespace {

// Use the same log facility as Chrome for convenience.
// {7FE69228-633E-4f06-80C1-527FEA23E3A7}
const GUID kChromeWatcherTraceProviderName = {
    0x7fe69228, 0x633e, 0x4f06,
        { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };

// The data shared from the main function to the console handler.
struct HandlerData {
  HandlerData() : handler_done(true /* manual_reset */,
                               false /* initially_signaled */) {
  }

  base::WaitableEvent handler_done;
  base::ProcessHandle process;
  const base::char16* registry_path;
};

HandlerData *g_handler_data = nullptr;

BOOL CALLBACK ConsoleCtrlHandler(DWORD ctl_type) {
  if (g_handler_data && ctl_type == CTRL_LOGOFF_EVENT) {
    // Record the watcher logoff event in the browser's exit funnel.
    browser_watcher::ExitFunnel funnel;
    funnel.Init(g_handler_data->registry_path, g_handler_data->process);
    funnel.RecordEvent(L"WatcherLogoff");

    // Release the main function.
    g_handler_data->handler_done.Signal();
  }

  // Fall through to the next handler in turn.
  return FALSE;
}

}  // namespace

// The main entry point to the watcher, declared as extern "C" to avoid name
// mangling.
extern "C" int WatcherMain(const base::char16* registry_path,
                           HANDLE process_handle) {
  base::Process process(process_handle);
  // The exit manager is in charge of calling the dtors of singletons.
  base::AtExitManager exit_manager;
  // Initialize the commandline singleton from the environment.
  base::CommandLine::Init(0, nullptr);

  logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName);

  // Arrange to be shut down as late as possible, as we want to outlive
  // chrome.exe in order to report its exit status.
  // TODO(siggi): Does this (windowless) process need to register a console
  //     handler too, in order to get notice of logoff events?
  ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);

  browser_watcher::ExitCodeWatcher exit_code_watcher(registry_path);
  int ret = 1;
  // Attempt to wait on our parent process, and record its exit status.
  if (exit_code_watcher.Initialize(process.Duplicate())) {
    // Set up a console control handler, and provide it the data it needs
    // to record into the browser's exit funnel.
    HandlerData data;
    data.process = process.Handle();
    data.registry_path = registry_path;
    g_handler_data = &data;
    ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE /* Add */);

    // Wait on the process.
    exit_code_watcher.WaitForExit();

    browser_watcher::ExitFunnel funnel;
    funnel.Init(registry_path, process.Handle());
    funnel.RecordEvent(L"BrowserExit");

    // Chrome/content exit codes are currently in the range of 0-28.
    // Anything outside this range is strange, so watch harder.
    int exit_code = exit_code_watcher.exit_code();
    if (exit_code < 0 || exit_code > 28) {
      // Wait for a max of 30 seconds to see whether we get notified of logoff.
      data.handler_done.TimedWait(base::TimeDelta::FromSeconds(30));
    }

    // Remove the console control handler.
    // There is a potential race here, where the handler might be executing
    // already as we fall through here. Hopefully SetConsoleCtrlHandler is
    // synchronizing against handler execution, for there's no other way to
    // close the race. Thankfully we'll just get snuffed out, as this is logoff.
    ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, FALSE /* Add */);
    g_handler_data = nullptr;

    ret = 0;
  }

  // Wind logging down.
  logging::LogEventProvider::Uninitialize();

  return ret;
}

static_assert(base::is_same<decltype(&WatcherMain),
                            browser_watcher::WatcherMainFunction>::value,
              "WatcherMain() has wrong type");
<commit_msg>Use a window to catch WM_ENDSESSION.<commit_after>// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <windows.h>

#include "base/at_exit.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/logging_win.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/template_util.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "components/browser_watcher/endsession_watcher_window_win.h"
#include "components/browser_watcher/exit_code_watcher_win.h"
#include "components/browser_watcher/exit_funnel_win.h"
#include "components/browser_watcher/watcher_main_api_win.h"

namespace {

// Use the same log facility as Chrome for convenience.
// {7FE69228-633E-4f06-80C1-527FEA23E3A7}
const GUID kChromeWatcherTraceProviderName = {
    0x7fe69228, 0x633e, 0x4f06,
        { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };

// Takes care of monitoring a browser. This class watches for a browser's exit
// code, as well as listening for WM_ENDSESSION messages. Events are recorded
// in an exit funnel, for reporting the next time Chrome runs.
class BrowserMonitor {
 public:
  BrowserMonitor(base::RunLoop* run_loop, const base::char16* registry_path);
  ~BrowserMonitor();

  // Starts the monitor, returns true on success.
  bool StartWatching(const base::char16* registry_path,
                     base::Process process);

 private:
  // Called from EndSessionWatcherWindow on a WM_ENDSESSION message.
  void OnEndSession(LPARAM lparam);

  // Blocking function that runs on |background_thread_|.
  void Watch();

  // Posted to main thread from Watch when browser exits.
  void BrowserExited();

  // True if BrowserExited has run.
  bool browser_exited_;

  // The funnel used to record events for this browser.
  browser_watcher::ExitFunnel exit_funnel_;

  browser_watcher::ExitCodeWatcher exit_code_watcher_;
  browser_watcher::EndSessionWatcherWindow end_session_watcher_window_;

  // The thread that runs Watch().
  base::Thread background_thread_;

  // The run loop for the main thread and its task runner.
  base::RunLoop* run_loop_;
  scoped_refptr<base::SequencedTaskRunner> main_thread_;

  DISALLOW_COPY_AND_ASSIGN(BrowserMonitor);
};

BrowserMonitor::BrowserMonitor(base::RunLoop* run_loop,
                               const base::char16* registry_path) :
    browser_exited_(false),
    exit_code_watcher_(registry_path),
    end_session_watcher_window_(
        base::Bind(&BrowserMonitor::OnEndSession, base::Unretained(this))),
    background_thread_("BrowserWatcherThread"),
    run_loop_(run_loop),
    main_thread_(base::MessageLoopProxy::current()) {
}

BrowserMonitor::~BrowserMonitor() {
}

bool BrowserMonitor::StartWatching(const base::char16* registry_path,
                                   base::Process process) {
  if (!exit_code_watcher_.Initialize(process.Pass()))
    return false;

  if (!exit_funnel_.Init(registry_path,
                        exit_code_watcher_.process().Handle())) {
    return false;
  }

  if (!background_thread_.StartWithOptions(
        base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) {
    return false;
  }

  if (!background_thread_.task_runner()->PostTask(FROM_HERE,
        base::Bind(&BrowserMonitor::Watch, base::Unretained(this)))) {
    background_thread_.Stop();
    return false;
  }

  return true;
}

void BrowserMonitor::OnEndSession(LPARAM lparam) {
  DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());

  exit_funnel_.RecordEvent(L"WatcherLogoff");

  // Belt-and-suspenders; make sure our message loop exits ASAP.
  if (browser_exited_)
    run_loop_->Quit();
}

void BrowserMonitor::Watch() {
  // This needs to run on an IO thread.
  DCHECK_NE(main_thread_, base::MessageLoopProxy::current());

  exit_code_watcher_.WaitForExit();
  exit_funnel_.RecordEvent(L"BrowserExit");

  main_thread_->PostTask(FROM_HERE,
      base::Bind(&BrowserMonitor::BrowserExited, base::Unretained(this)));
}

void BrowserMonitor::BrowserExited() {
  // This runs in the main thread.
  DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());

  // Note that the browser has exited.
  browser_exited_ = true;

  // Our background thread has served it's purpose.
  background_thread_.Stop();

  const int exit_code = exit_code_watcher_.exit_code();
  if (exit_code >= 0 && exit_code <= 28) {
    // The browser exited with a well-known exit code, quit this process
    // immediately.
    run_loop_->Quit();
  } else {
    // The browser exited abnormally, wait around for a little bit to see
    // whether this instance will get a logoff message.
    main_thread_->PostDelayedTask(FROM_HERE,
                                  run_loop_->QuitClosure(),
                                  base::TimeDelta::FromSeconds(30));
  }
}

}  // namespace

// The main entry point to the watcher, declared as extern "C" to avoid name
// mangling.
extern "C" int WatcherMain(const base::char16* registry_path,
                           HANDLE process_handle) {
  base::Process process(process_handle);

  // The exit manager is in charge of calling the dtors of singletons.
  base::AtExitManager exit_manager;
  // Initialize the commandline singleton from the environment.
  base::CommandLine::Init(0, nullptr);

  logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName);

  // Arrange to be shut down as late as possible, as we want to outlive
  // chrome.exe in order to report its exit status.
  ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);

  // Run a UI message loop on the main thread.
  base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI);
  msg_loop.set_thread_name("WatcherMainThread");

  base::RunLoop run_loop;
  BrowserMonitor monitor(&run_loop, registry_path);
  if (!monitor.StartWatching(registry_path, process.Pass()))
    return 1;

  run_loop.Run();

  // Wind logging down.
  logging::LogEventProvider::Uninitialize();

  return 0;
}

static_assert(base::is_same<decltype(&WatcherMain),
                            browser_watcher::WatcherMainFunction>::value,
              "WatcherMain() has wrong type");
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.  Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

#include "chrome/renderer/media/video_renderer_impl.h"
#include "media/base/buffers.h"
#include "media/base/yuv_convert.h"

VideoRendererImpl::VideoRendererImpl(WebMediaPlayerImpl* delegate)
    : delegate_(delegate),
      last_converted_frame_(NULL) {
  // TODO(hclam): decide whether to do the following line in this thread or
  // in the render thread.
  delegate_->SetVideoRenderer(this);
}

// static
bool VideoRendererImpl::IsMediaFormatSupported(
    const media::MediaFormat& media_format) {
  int width = 0;
  int height = 0;
  return ParseMediaFormat(media_format, &width, &height);
}

void VideoRendererImpl::Stop() {
  VideoThread::Stop();
  delegate_->SetVideoRenderer(NULL);
}

bool VideoRendererImpl::OnInitialize(media::VideoDecoder* decoder) {
  int width = 0;
  int height = 0;
  if (!ParseMediaFormat(decoder->media_format(), &width, &height))
    return false;

  video_size_.SetSize(width, height);
  bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height);
  if (bitmap_.allocPixels(NULL, NULL)) {
    bitmap_.eraseRGB(0x00, 0x00, 0x00);
    return true;
  }

  NOTREACHED();
  return false;
}

void VideoRendererImpl::SetRect(const gfx::Rect& rect) {}

void VideoRendererImpl::OnFrameAvailable() {
  delegate_->PostRepaintTask();
}

// This method is always called on the renderer's thread.
void VideoRendererImpl::Paint(skia::PlatformCanvas* canvas,
                              const gfx::Rect& dest_rect) {
  scoped_refptr<media::VideoFrame> video_frame;
  GetCurrentFrame(&video_frame);
  if (video_frame) {
    if (CanFastPaint(canvas, dest_rect)) {
      FastPaint(video_frame, canvas, dest_rect);
    } else {
      SlowPaint(video_frame, canvas, dest_rect);
    }
    video_frame = NULL;
  }
}

// CanFastPaint is a helper method to determine the conditions for fast
// painting. The conditions are:
//   1. No skew in canvas matrix.
//   2. Canvas has pixel format ARGB8888.
//   3. Canvas is opaque.
bool VideoRendererImpl::CanFastPaint(skia::PlatformCanvas* canvas,
                                     const gfx::Rect& dest_rect) {
  const SkMatrix& total_matrix = canvas->getTotalMatrix();
  if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
      SkScalarNearlyZero(total_matrix.getSkewY())) {
    // Get the properties of the SkDevice and the clip rect.
    SkDevice* device = canvas->getDevice();

    // Get the boundary of the device.
    SkIRect device_rect;
    device->getBounds(&device_rect);

    // Get the pixel config of the device.
    const SkBitmap::Config config = device->config();
    // Get the total clip rect associated with the canvas.
    const SkRegion& total_clip = canvas->getTotalClip();

    SkIRect dest_irect;
    TransformToSkIRect(canvas->getTotalMatrix(), dest_rect, &dest_irect);

    if (config == SkBitmap::kARGB_8888_Config && device->isOpaque() &&
        device_rect.contains(total_clip.getBounds())) {
      return true;
    }
  }
  return false;
}

void VideoRendererImpl::SlowPaint(media::VideoFrame* video_frame,
                                  skia::PlatformCanvas* canvas,
                                  const gfx::Rect& dest_rect) {
  // 1. Convert YUV frame to RGB.
  base::TimeDelta timestamp = video_frame->GetTimestamp();
  if (video_frame != last_converted_frame_ ||
      timestamp != last_converted_timestamp_) {
    last_converted_frame_ = video_frame;
    last_converted_timestamp_ = timestamp;
    media::VideoSurface frame_in;
    if (video_frame->Lock(&frame_in)) {
      // TODO(hclam): Support more video formats than just YV12.
      DCHECK(frame_in.format == media::VideoSurface::YV12);
      DCHECK(frame_in.strides[media::VideoSurface::kUPlane] ==
             frame_in.strides[media::VideoSurface::kVPlane]);
      DCHECK(frame_in.planes == media::VideoSurface::kNumYUVPlanes);
      bitmap_.lockPixels();
      media::ConvertYUVToRGB32(frame_in.data[media::VideoSurface::kYPlane],
                               frame_in.data[media::VideoSurface::kUPlane],
                               frame_in.data[media::VideoSurface::kVPlane],
                               static_cast<uint8*>(bitmap_.getPixels()),
                               frame_in.width,
                               frame_in.height,
                               frame_in.strides[media::VideoSurface::kYPlane],
                               frame_in.strides[media::VideoSurface::kUPlane],
                               bitmap_.rowBytes(),
                               media::YV12);
      bitmap_.unlockPixels();
      video_frame->Unlock();
    } else {
      NOTREACHED();
    }
  }

  // 2. Paint the bitmap to canvas.
  SkMatrix matrix;
  matrix.setTranslate(static_cast<SkScalar>(dest_rect.x()),
                      static_cast<SkScalar>(dest_rect.y()));
  if (dest_rect.width()  != video_size_.width() ||
      dest_rect.height() != video_size_.height()) {
    matrix.preScale(SkIntToScalar(dest_rect.width()) /
                    SkIntToScalar(video_size_.width()),
                    SkIntToScalar(dest_rect.height()) /
                    SkIntToScalar(video_size_.height()));
  }
  canvas->drawBitmapMatrix(bitmap_, matrix, NULL);
}

void VideoRendererImpl::FastPaint(media::VideoFrame* video_frame,
                                  skia::PlatformCanvas* canvas,
                                  const gfx::Rect& dest_rect) {
  media::VideoSurface frame_in;
  if (video_frame->Lock(&frame_in)) {
    // TODO(hclam): Support more video formats than just YV12.
    DCHECK(frame_in.format == media::VideoSurface::YV12);
    DCHECK(frame_in.strides[media::VideoSurface::kUPlane] ==
           frame_in.strides[media::VideoSurface::kVPlane]);
    DCHECK(frame_in.planes == media::VideoSurface::kNumYUVPlanes);
    const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);

    // Create a rectangle backed by SkScalar.
    SkRect scalar_dest_rect;
    scalar_dest_rect.iset(dest_rect.x(), dest_rect.y(),
                          dest_rect.right(), dest_rect.bottom());

    // Transform the destination rectangle to local coordinates.
    const SkMatrix& local_matrix = canvas->getTotalMatrix();
    SkRect local_dest_rect;
    local_matrix.mapRect(&local_dest_rect, scalar_dest_rect);

    // After projecting the destination rectangle to local coordinates, round
    // the projected rectangle to integer values, this will give us pixel values
    // of the rectangle.
    SkIRect local_dest_irect, local_dest_irect_saved;
    local_dest_rect.round(&local_dest_irect);
    local_dest_rect.round(&local_dest_irect_saved);

    // Only does the paint if the destination rect intersects with the clip
    // rect.
    if (local_dest_irect.intersect(canvas->getTotalClip().getBounds())) {
      // At this point |local_dest_irect| contains the rect that we should draw
      // to within the clipping rect.

      // Calculate the address for the top left corner of destination rect in
      // the canvas that we will draw to. The address is obtained by the base
      // address of the canvas shifted by "left" and "top" of the rect.
      uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
                                 local_dest_irect.fTop * bitmap.rowBytes() +
                                 local_dest_irect.fLeft * 4;

      // Project the clip rect to the original video frame, obtains the
      // dimensions of the projected clip rect, "left" and "top" of the rect.
      // The math here are all integer math so we won't have rounding error and
      // write outside of the canvas.
      // We have the assumptions of dest_rect.width() and dest_rect.height()
      // being non-zero, these are valid assumptions since finding intersection
      // above rejects empty rectangle so we just do a DCHECK here.
      DCHECK_NE(0, dest_rect.width());
      DCHECK_NE(0, dest_rect.height());
      size_t frame_clip_width = local_dest_irect.width() *
                                frame_in.width / dest_rect.width();
      size_t frame_clip_height = local_dest_irect.height() *
                                 frame_in.height / dest_rect.height();

      // Project the "left" and "top" of the final destination rect to local
      // coordinates of the video frame, use these values to find the offsets
      // in the video frame to start reading.
      size_t frame_clip_left = (local_dest_irect.fLeft -
                                local_dest_irect_saved.fLeft) *
                               frame_in.width / dest_rect.width();
      size_t frame_clip_top = (local_dest_irect.fTop -
                               local_dest_irect_saved.fTop) *
                              frame_in.height / dest_rect.height();

      // Use the "left" and "top" of the destination rect to locate the offset
      // in Y, U and V planes.
      size_t y_offset = frame_in.strides[media::VideoSurface::kYPlane] *
                        frame_clip_top + frame_clip_left;
      // Since the format is YV12, there is one U, V value per 2x2 block, thus
      // the math here.
      // TODO(hclam): handle formats other than YV12.
      size_t uv_offset = (frame_in.strides[media::VideoSurface::kUPlane] *
                          frame_clip_top + frame_clip_left) / 2;
      uint8* frame_clip_y = frame_in.data[media::VideoSurface::kYPlane] +
                            y_offset;
      uint8* frame_clip_u = frame_in.data[media::VideoSurface::kUPlane] +
                            uv_offset;
      uint8* frame_clip_v = frame_in.data[media::VideoSurface::kVPlane] +
                            uv_offset;
      bitmap.lockPixels();
      // TODO(hclam): do rotation and mirroring here.
      media::ScaleYUVToRGB32(frame_clip_y,
                             frame_clip_u,
                             frame_clip_v,
                             dest_rect_pointer,
                             frame_clip_width,
                             frame_clip_height,
                             local_dest_irect.width(),
                             local_dest_irect.height(),
                             frame_in.strides[media::VideoSurface::kYPlane],
                             frame_in.strides[media::VideoSurface::kUPlane],
                             bitmap.rowBytes(),
                             media::YV12,
                             media::ROTATE_0);
      bitmap.unlockPixels();
    }
    video_frame->Unlock();
  } else {
    NOTREACHED();
  }
}

void VideoRendererImpl::TransformToSkIRect(const SkMatrix& matrix,
                                           const gfx::Rect& src_rect,
                                           SkIRect* dest_rect) {
    // Transform destination rect to local coordinates.
    SkRect transformed_rect;
    SkRect skia_dest_rect;
    skia_dest_rect.iset(src_rect.x(), src_rect.y(),
                        src_rect.right(), src_rect.bottom());
    matrix.mapRect(&transformed_rect, skia_dest_rect);
    transformed_rect.round(dest_rect);
}
<commit_msg>Fix a misaligmnet in rendering video frames<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.  Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

#include "chrome/renderer/media/video_renderer_impl.h"
#include "media/base/buffers.h"
#include "media/base/yuv_convert.h"

VideoRendererImpl::VideoRendererImpl(WebMediaPlayerImpl* delegate)
    : delegate_(delegate),
      last_converted_frame_(NULL) {
  // TODO(hclam): decide whether to do the following line in this thread or
  // in the render thread.
  delegate_->SetVideoRenderer(this);
}

// static
bool VideoRendererImpl::IsMediaFormatSupported(
    const media::MediaFormat& media_format) {
  int width = 0;
  int height = 0;
  return ParseMediaFormat(media_format, &width, &height);
}

void VideoRendererImpl::Stop() {
  VideoThread::Stop();
  delegate_->SetVideoRenderer(NULL);
}

bool VideoRendererImpl::OnInitialize(media::VideoDecoder* decoder) {
  int width = 0;
  int height = 0;
  if (!ParseMediaFormat(decoder->media_format(), &width, &height))
    return false;

  video_size_.SetSize(width, height);
  bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height);
  if (bitmap_.allocPixels(NULL, NULL)) {
    bitmap_.eraseRGB(0x00, 0x00, 0x00);
    return true;
  }

  NOTREACHED();
  return false;
}

void VideoRendererImpl::SetRect(const gfx::Rect& rect) {}

void VideoRendererImpl::OnFrameAvailable() {
  delegate_->PostRepaintTask();
}

// This method is always called on the renderer's thread.
void VideoRendererImpl::Paint(skia::PlatformCanvas* canvas,
                              const gfx::Rect& dest_rect) {
  scoped_refptr<media::VideoFrame> video_frame;
  GetCurrentFrame(&video_frame);
  if (video_frame) {
    if (CanFastPaint(canvas, dest_rect)) {
      FastPaint(video_frame, canvas, dest_rect);
    } else {
      SlowPaint(video_frame, canvas, dest_rect);
    }
    video_frame = NULL;
  }
}

// CanFastPaint is a helper method to determine the conditions for fast
// painting. The conditions are:
//   1. No skew in canvas matrix.
//   2. Canvas has pixel format ARGB8888.
//   3. Canvas is opaque.
bool VideoRendererImpl::CanFastPaint(skia::PlatformCanvas* canvas,
                                     const gfx::Rect& dest_rect) {
  const SkMatrix& total_matrix = canvas->getTotalMatrix();
  if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
      SkScalarNearlyZero(total_matrix.getSkewY())) {
    // Get the properties of the SkDevice and the clip rect.
    SkDevice* device = canvas->getDevice();

    // Get the boundary of the device.
    SkIRect device_rect;
    device->getBounds(&device_rect);

    // Get the pixel config of the device.
    const SkBitmap::Config config = device->config();
    // Get the total clip rect associated with the canvas.
    const SkRegion& total_clip = canvas->getTotalClip();

    SkIRect dest_irect;
    TransformToSkIRect(canvas->getTotalMatrix(), dest_rect, &dest_irect);

    if (config == SkBitmap::kARGB_8888_Config && device->isOpaque() &&
        device_rect.contains(total_clip.getBounds())) {
      return true;
    }
  }
  return false;
}

void VideoRendererImpl::SlowPaint(media::VideoFrame* video_frame,
                                  skia::PlatformCanvas* canvas,
                                  const gfx::Rect& dest_rect) {
  // 1. Convert YUV frame to RGB.
  base::TimeDelta timestamp = video_frame->GetTimestamp();
  if (video_frame != last_converted_frame_ ||
      timestamp != last_converted_timestamp_) {
    last_converted_frame_ = video_frame;
    last_converted_timestamp_ = timestamp;
    media::VideoSurface frame_in;
    if (video_frame->Lock(&frame_in)) {
      // TODO(hclam): Support more video formats than just YV12.
      DCHECK(frame_in.format == media::VideoSurface::YV12);
      DCHECK(frame_in.strides[media::VideoSurface::kUPlane] ==
             frame_in.strides[media::VideoSurface::kVPlane]);
      DCHECK(frame_in.planes == media::VideoSurface::kNumYUVPlanes);
      bitmap_.lockPixels();
      media::ConvertYUVToRGB32(frame_in.data[media::VideoSurface::kYPlane],
                               frame_in.data[media::VideoSurface::kUPlane],
                               frame_in.data[media::VideoSurface::kVPlane],
                               static_cast<uint8*>(bitmap_.getPixels()),
                               frame_in.width,
                               frame_in.height,
                               frame_in.strides[media::VideoSurface::kYPlane],
                               frame_in.strides[media::VideoSurface::kUPlane],
                               bitmap_.rowBytes(),
                               media::YV12);
      bitmap_.unlockPixels();
      video_frame->Unlock();
    } else {
      NOTREACHED();
    }
  }

  // 2. Paint the bitmap to canvas.
  SkMatrix matrix;
  matrix.setTranslate(static_cast<SkScalar>(dest_rect.x()),
                      static_cast<SkScalar>(dest_rect.y()));
  if (dest_rect.width()  != video_size_.width() ||
      dest_rect.height() != video_size_.height()) {
    matrix.preScale(SkIntToScalar(dest_rect.width()) /
                    SkIntToScalar(video_size_.width()),
                    SkIntToScalar(dest_rect.height()) /
                    SkIntToScalar(video_size_.height()));
  }
  canvas->drawBitmapMatrix(bitmap_, matrix, NULL);
}

void VideoRendererImpl::FastPaint(media::VideoFrame* video_frame,
                                  skia::PlatformCanvas* canvas,
                                  const gfx::Rect& dest_rect) {
  media::VideoSurface frame_in;
  if (video_frame->Lock(&frame_in)) {
    // TODO(hclam): Support more video formats than just YV12.
    DCHECK(frame_in.format == media::VideoSurface::YV12);
    DCHECK(frame_in.strides[media::VideoSurface::kUPlane] ==
           frame_in.strides[media::VideoSurface::kVPlane]);
    DCHECK(frame_in.planes == media::VideoSurface::kNumYUVPlanes);
    const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);

    // Create a rectangle backed by SkScalar.
    SkRect scalar_dest_rect;
    scalar_dest_rect.iset(dest_rect.x(), dest_rect.y(),
                          dest_rect.right(), dest_rect.bottom());

    // Transform the destination rectangle to local coordinates.
    const SkMatrix& local_matrix = canvas->getTotalMatrix();
    SkRect local_dest_rect;
    local_matrix.mapRect(&local_dest_rect, scalar_dest_rect);

    // After projecting the destination rectangle to local coordinates, round
    // the projected rectangle to integer values, this will give us pixel values
    // of the rectangle.
    SkIRect local_dest_irect, local_dest_irect_saved;
    local_dest_rect.round(&local_dest_irect);
    local_dest_rect.round(&local_dest_irect_saved);

    // Only does the paint if the destination rect intersects with the clip
    // rect.
    if (local_dest_irect.intersect(canvas->getTotalClip().getBounds())) {
      // At this point |local_dest_irect| contains the rect that we should draw
      // to within the clipping rect.

      // Calculate the address for the top left corner of destination rect in
      // the canvas that we will draw to. The address is obtained by the base
      // address of the canvas shifted by "left" and "top" of the rect.
      uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
                                 local_dest_irect.fTop * bitmap.rowBytes() +
                                 local_dest_irect.fLeft * 4;

      // Project the clip rect to the original video frame, obtains the
      // dimensions of the projected clip rect, "left" and "top" of the rect.
      // The math here are all integer math so we won't have rounding error and
      // write outside of the canvas.
      // We have the assumptions of dest_rect.width() and dest_rect.height()
      // being non-zero, these are valid assumptions since finding intersection
      // above rejects empty rectangle so we just do a DCHECK here.
      DCHECK_NE(0, dest_rect.width());
      DCHECK_NE(0, dest_rect.height());
      size_t frame_clip_width = local_dest_irect.width() *
                                frame_in.width / dest_rect.width();
      size_t frame_clip_height = local_dest_irect.height() *
                                 frame_in.height / dest_rect.height();

      // Project the "left" and "top" of the final destination rect to local
      // coordinates of the video frame, use these values to find the offsets
      // in the video frame to start reading.
      size_t frame_clip_left = (local_dest_irect.fLeft -
                                local_dest_irect_saved.fLeft) *
                               frame_in.width / dest_rect.width();
      size_t frame_clip_top = (local_dest_irect.fTop -
                               local_dest_irect_saved.fTop) *
                              frame_in.height / dest_rect.height();

      // Use the "left" and "top" of the destination rect to locate the offset
      // in Y, U and V planes.
      size_t y_offset = frame_in.strides[media::VideoSurface::kYPlane] *
                        frame_clip_top + frame_clip_left;
      // Since the format is YV12, there is one U, V value per 2x2 block, thus
      // the math here.
      // TODO(hclam): handle formats other than YV12.
      size_t uv_offset = frame_in.strides[media::VideoSurface::kUPlane] *
                         (frame_clip_top / 2) + frame_clip_left / 2;
      uint8* frame_clip_y = frame_in.data[media::VideoSurface::kYPlane] +
                            y_offset;
      uint8* frame_clip_u = frame_in.data[media::VideoSurface::kUPlane] +
                            uv_offset;
      uint8* frame_clip_v = frame_in.data[media::VideoSurface::kVPlane] +
                            uv_offset;
      bitmap.lockPixels();
      // TODO(hclam): do rotation and mirroring here.
      media::ScaleYUVToRGB32(frame_clip_y,
                             frame_clip_u,
                             frame_clip_v,
                             dest_rect_pointer,
                             frame_clip_width,
                             frame_clip_height,
                             local_dest_irect.width(),
                             local_dest_irect.height(),
                             frame_in.strides[media::VideoSurface::kYPlane],
                             frame_in.strides[media::VideoSurface::kUPlane],
                             bitmap.rowBytes(),
                             media::YV12,
                             media::ROTATE_0);
      bitmap.unlockPixels();
    }
    video_frame->Unlock();
  } else {
    NOTREACHED();
  }
}

void VideoRendererImpl::TransformToSkIRect(const SkMatrix& matrix,
                                           const gfx::Rect& src_rect,
                                           SkIRect* dest_rect) {
    // Transform destination rect to local coordinates.
    SkRect transformed_rect;
    SkRect skia_dest_rect;
    skia_dest_rect.iset(src_rect.x(), src_rect.y(),
                        src_rect.right(), src_rect.bottom());
    matrix.mapRect(&transformed_rect, skia_dest_rect);
    transformed_rect.round(dest_rect);
}
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
// copyright 2012, 2013, 2014 Keean Schupke
// compile with -std=c++11
// templateio.h

#ifndef TEMPLATEIO_HPP
#define TEMPLATEIO_HPP

#include <iostream>
#include <vector>
#include <tuple>

using namespace std;

//----------------------------------------------------------------------------
// iostream output for tuples.

template <size_t> struct idx {};

template <typename T, size_t I> ostream& print_tuple(ostream& out, T const& t, idx<I>) {
    out << get<tuple_size<T>::value - I>(t) << ", ";
    return print_tuple(out, t, idx<I - 1>());
}

template <typename T> ostream& print_tuple(ostream& out, T const& t, idx<1>) {
    return out << get<tuple_size<T>::value - 1>(t);
}

template <typename... T> ostream& operator<< (ostream& out, tuple<T...> const& t) {
    out << '<';
    print_tuple(out, t, idx<sizeof...(T)>());
    return out << '>';
}

//----------------------------------------------------------------------------
// iostream output for vectors.

template <typename T> ostream& operator<< (ostream& out, vector<T> const& v) {
    out << "[";
    for (typename vector<T>::const_iterator i = v.begin(); i != v.end(); ++i) {
        cout << *i;
        typename vector<T>::const_iterator j = i;
        if (++j != v.end()) {
            cout << ", ";
        }
    }
    return out << "]";
}

#endif // TEMPLATEIO_HPP
<commit_msg>add templateio for maps<commit_after>//----------------------------------------------------------------------------
// copyright 2012, 2013, 2014 Keean Schupke
// compile with -std=c++11
// templateio.h

#ifndef TEMPLATEIO_HPP
#define TEMPLATEIO_HPP

#include <iostream>
#include <vector>
#include <tuple>
#include <map>

using namespace std;

//----------------------------------------------------------------------------
// iostream output for tuples.

template <size_t> struct idx {};

template <typename T, size_t I> ostream& print_tuple(ostream& out, T const& t, idx<I>) {
    out << get<tuple_size<T>::value - I>(t) << ", ";
    return print_tuple(out, t, idx<I - 1>());
}

template <typename T> ostream& print_tuple(ostream& out, T const& t, idx<1>) {
    return out << get<tuple_size<T>::value - 1>(t);
}

template <typename... T> ostream& operator<< (ostream& out, tuple<T...> const& t) {
    out << '<';
    print_tuple(out, t, idx<sizeof...(T)>());
    return out << '>';
}

//----------------------------------------------------------------------------
// iostream output for vectors.

template <typename T> ostream& operator<< (ostream& out, vector<T> const& v) {
    out << "[";
    for (typename vector<T>::const_iterator i = v.begin(); i != v.end(); ++i) {
        cout << *i;
        typename vector<T>::const_iterator j = i;
        if (++j != v.end()) {
            cout << ", ";
        }
    }
    return out << "]";
}

//----------------------------------------------------------------------------
// iostream output for maps.

template <typename S, typename T> ostream& operator<< (ostream& out, map<S, T> const& m) {
    out << "{";
    for (typename map<S, T>::const_iterator i = m.begin(); i != m.end(); ++i) {
        cout << i->first << " = " << i->second;
        typename map<S, T>::const_iterator j = i;
        if (++j != m.end()) {
            cout << ", ";
        }
    }
    return out << "]";
}

#endif // TEMPLATEIO_HPP
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 koolkdev
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#include "Wfs.h"
#include "DeviceEncryption.h"
#include "Area.h"
#include "Directory.h"
#include "FileDevice.h"
#include "MetadataBlock.h"
#include "Structs.h"

#include <boost/filesystem.hpp>

Wfs::Wfs(const std::shared_ptr<Device>& device, std::vector<uint8_t>& key) : device(std::make_shared<DeviceEncryption>(device, key)) {
	// Read first area
	this->root_area = Area::LoadRootArea(this->device);
}

std::shared_ptr<WfsItem> Wfs::GetObject(const std::string& filename) {
	if (filename == "/") return GetDirectory("/");
	boost::filesystem::path path(filename);
	auto dir = GetDirectory(path.parent_path().string());
	if (!dir) return std::shared_ptr<WfsItem>();
	return dir->GetObject(path.filename().string());
}

std::shared_ptr<File> Wfs::GetFile(const std::string& filename) {
	boost::filesystem::path path(filename);
	auto dir = GetDirectory(path.parent_path().string());
	if (!dir) return std::shared_ptr<File>();
	return dir->GetFile(path.filename().string());
}

std::shared_ptr<Directory> Wfs::GetDirectory(const std::string& filename) {
	boost::filesystem::path path(filename);
	std::shared_ptr<Directory> current_directory = this->root_area->GetRootDirectory();

	for (auto& part : path) {
		// the first part is "/"
		if (part == "/") continue;
		current_directory = current_directory->GetDirectory(part.string());
		if (!current_directory) return current_directory;
	}
	return current_directory;
}

void Wfs::DetectDeviceSectorSizeAndCount(const std::shared_ptr<FileDevice>& device, const std::vector<uint8_t>& key) {
	// The encryption of the blocks depends on the device sector size and count, which builds the IV
	// We are going to find out the correct first 0x10 bytes, and than xor it with what we read to find out the correct IV
	// From that IV we extract the sectors count and sector size of the device.
	// Bytes 0-4 are going to be correct because the first 4 bytes of the IV is the block size, which we read from the first block.
	// The other bytes are part of the hash, so we will get them once we calculate the hash. So once we get the correct hash we find the correct IV.
	// So it will fail hash check
	// Let's read the first block first, ignore the hash and check the wfs version, and read the block size
	// But lets set the sectors size to 9 and the sector size to 0x10, because this is the max we are going to read right now
	device->SetSectorsCount(0x10);
	device->SetLog2SectorSize(9);
	auto enc_device = std::make_shared<DeviceEncryption>(device, key);
	auto block = MetadataBlock::LoadBlock(enc_device, 0, Block::BlockSize::Basic, 0, false);
	auto wfs_header = reinterpret_cast<WfsHeader *>(&block->GetData()[sizeof(MetadataBlockHeader)]);
	if (wfs_header->version.value() != 0x01010800)
		throw std::runtime_error("Unexpected WFS version (bad key?)");
	auto block_size = Block::BlockSize::Basic;
	if (!(wfs_header->root_area_attributes.flags.value() & wfs_header->root_area_attributes.Flags::AREA_SIZE_BASIC) && (wfs_header->root_area_attributes.flags.value() & wfs_header->root_area_attributes.Flags::AREA_SIZE_REGULAR))
		block_size = Block::BlockSize::Regular;
	// Now lets read it again, this time with the correct block size
	block = MetadataBlock::LoadBlock(enc_device, 0, block_size, 0, false);
	uint32_t xored_sectors_count, xored_sector_size;
	// The two last dwords of the IV is the sectors count and sector size, right now it is xored with our fake sector size and sector count, and with the hash
	auto& data = block->GetData();
	auto first_4_dwords = reinterpret_cast<boost::endian::big_uint32_buf_t*>(&data[0]);
	xored_sectors_count = first_4_dwords[2].value();
	xored_sector_size = first_4_dwords[3].value();
	// Lets calculate the hash of the block
	enc_device->CalculateHash(data, data.begin() + offsetof(MetadataBlockHeader, hash), true);
	// Now xor it with the real hash
	xored_sectors_count ^= first_4_dwords[2].value();
	xored_sector_size ^= first_4_dwords[3].value();
	// And xor it with our fake sectors count and block size
	xored_sectors_count ^= 0x10;
	xored_sector_size ^= 1 << 9;
	device->SetLog2SectorSize(static_cast<uint32_t>(log2(xored_sector_size)));
	device->SetSectorsCount(xored_sectors_count);
	// Now try to fetch block again, this time check the hash, it will raise exception
	try {
		block = MetadataBlock::LoadBlock(enc_device, 0, block_size, 0, true);
	}
	catch (Block::BadHash) {
		throw std::runtime_error("Wfs: Failed to detect sector size and sectors count");
	}
}
<commit_msg>wfslib: calculate and verify log2 of the sector size (fixes #3)<commit_after>/*
 * Copyright (C) 2017 koolkdev
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#include "Wfs.h"
#include "DeviceEncryption.h"
#include "Area.h"
#include "Directory.h"
#include "FileDevice.h"
#include "MetadataBlock.h"
#include "Structs.h"

#include <boost/filesystem.hpp>

Wfs::Wfs(const std::shared_ptr<Device>& device, std::vector<uint8_t>& key) : device(std::make_shared<DeviceEncryption>(device, key)) {
	// Read first area
	this->root_area = Area::LoadRootArea(this->device);
}

std::shared_ptr<WfsItem> Wfs::GetObject(const std::string& filename) {
	if (filename == "/") return GetDirectory("/");
	boost::filesystem::path path(filename);
	auto dir = GetDirectory(path.parent_path().string());
	if (!dir) return std::shared_ptr<WfsItem>();
	return dir->GetObject(path.filename().string());
}

std::shared_ptr<File> Wfs::GetFile(const std::string& filename) {
	boost::filesystem::path path(filename);
	auto dir = GetDirectory(path.parent_path().string());
	if (!dir) return std::shared_ptr<File>();
	return dir->GetFile(path.filename().string());
}

std::shared_ptr<Directory> Wfs::GetDirectory(const std::string& filename) {
	boost::filesystem::path path(filename);
	std::shared_ptr<Directory> current_directory = this->root_area->GetRootDirectory();

	for (auto& part : path) {
		// the first part is "/"
		if (part == "/") continue;
		current_directory = current_directory->GetDirectory(part.string());
		if (!current_directory) return current_directory;
	}
	return current_directory;
}

void Wfs::DetectDeviceSectorSizeAndCount(const std::shared_ptr<FileDevice>& device, const std::vector<uint8_t>& key) {
	// The encryption of the blocks depends on the device sector size and count, which builds the IV
	// We are going to find out the correct first 0x10 bytes, and than xor it with what we read to find out the correct IV
	// From that IV we extract the sectors count and sector size of the device.
	// Bytes 0-4 are going to be correct because the first 4 bytes of the IV is the block size, which we read from the first block.
	// The other bytes are part of the hash, so we will get them once we calculate the hash. So once we get the correct hash we find the correct IV.
	// So it will fail hash check
	// Let's read the first block first, ignore the hash and check the wfs version, and read the block size
	// But lets set the sectors size to 9 and the sector size to 0x10, because this is the max we are going to read right now
	device->SetSectorsCount(0x10);
	device->SetLog2SectorSize(9);
	auto enc_device = std::make_shared<DeviceEncryption>(device, key);
	auto block = MetadataBlock::LoadBlock(enc_device, 0, Block::BlockSize::Basic, 0, false);
	auto wfs_header = reinterpret_cast<WfsHeader *>(&block->GetData()[sizeof(MetadataBlockHeader)]);
	if (wfs_header->version.value() != 0x01010800)
		throw std::runtime_error("Unexpected WFS version (bad key?)");
	auto block_size = Block::BlockSize::Basic;
	if (!(wfs_header->root_area_attributes.flags.value() & wfs_header->root_area_attributes.Flags::AREA_SIZE_BASIC) && (wfs_header->root_area_attributes.flags.value() & wfs_header->root_area_attributes.Flags::AREA_SIZE_REGULAR))
		block_size = Block::BlockSize::Regular;
	// Now lets read it again, this time with the correct block size
	block = MetadataBlock::LoadBlock(enc_device, 0, block_size, 0, false);
	uint32_t xored_sectors_count, xored_sector_size;
	// The two last dwords of the IV is the sectors count and sector size, right now it is xored with our fake sector size and sector count, and with the hash
	auto& data = block->GetData();
	auto first_4_dwords = reinterpret_cast<boost::endian::big_uint32_buf_t*>(&data[0]);
	xored_sectors_count = first_4_dwords[2].value();
	xored_sector_size = first_4_dwords[3].value();
	// Lets calculate the hash of the block
	enc_device->CalculateHash(data, data.begin() + offsetof(MetadataBlockHeader, hash), true);
	// Now xor it with the real hash
	xored_sectors_count ^= first_4_dwords[2].value();
	xored_sector_size ^= first_4_dwords[3].value();
	// And xor it with our fake sectors count and block size
	xored_sectors_count ^= 0x10;
	xored_sector_size ^= 1 << 9;
	uint32_t sector_size = 0;
	while (!(xored_sector_size & 1)) {
		xored_sector_size >>= 1;
		sector_size++;
	}
	if (xored_sector_size >> 1) {
		// Not pow of 2
		throw std::runtime_error("Wfs: Failed to detect sector size and sectors count");
	}
	device->SetLog2SectorSize(sector_size);
	device->SetSectorsCount(xored_sectors_count);
	// Now try to fetch block again, this time check the hash, it will raise exception
	try {
		block = MetadataBlock::LoadBlock(enc_device, 0, block_size, 0, true);
	}
	catch (Block::BadHash) {
		throw std::runtime_error("Wfs: Failed to detect sector size and sectors count");
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iomanip>
#include <numeric>  // std::iota

#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"

// Solve a job shop problem:

namespace operations_research {
void SolveJobShopExample() {
  // Instantiate the solver.
  Solver solver("JobShopExample");
  std::array<int, 3> machines;
  std::iota(std::begin(machines), std::end(machines), 0);
  {
    std::ostringstream oss;
    for (auto i : machines) oss << ' ' << i;
    LOG(INFO) << "Machines: " << oss.str();
  }

  // Jobs definition
  using MachineIndex = int;
  using ProcessingTime = int;
  using Task = std::pair<MachineIndex, ProcessingTime>;
  using Job = std::vector<Task>;
  std::vector<Job> jobs = {
      {{0, 3}, {1, 2}, {2, 2}}, {{0, 2}, {2, 1}, {1, 4}}, {{1, 4}, {2, 3}}};
  LOG(INFO) << "Jobs:";
  for (int i = 0; i < jobs.size(); ++i) {
    std::ostringstream problem;
    problem << "Job " << i << ": [";
    for (const Task& task : jobs[i]) {
      problem << "(" << task.first << ", " << task.second << ")";
    }
    problem << "]" << std::endl;
    LOG(INFO) << problem.str();
  }

  // Computes horizon.
  ProcessingTime horizon = 0;
  for (const Job& job : jobs) {
    for (const Task& task : job) {
      horizon += task.second;
    }
  }
  LOG(INFO) << "Horizon: " << horizon;

  // Creates tasks.
  std::vector<std::vector<IntervalVar*>> tasks_matrix(jobs.size());
  for (int i = 0; i < jobs.size(); ++i) {
    for (int j = 0; j < jobs[i].size(); ++j) {
      std::ostringstream oss;
      oss << "Job_" << i << "_" << j;
      tasks_matrix[i].push_back(solver.MakeFixedDurationIntervalVar(
          0, horizon, jobs[i][j].second, false, oss.str()));
    }
  }

  // Add conjunctive contraints.
  for (int i = 0; i < jobs.size(); ++i) {
    for (int j = 0; j < jobs[i].size() - 1; ++j) {
      solver.AddConstraint(solver.MakeIntervalVarRelation(
          tasks_matrix[i][j + 1], Solver::STARTS_AFTER_END,
          tasks_matrix[i][j]));
    }
  }

  // Creates sequence variables and add disjunctive constraints.
  std::vector<SequenceVar*> all_sequences;
  std::vector<IntVar*> all_machines_jobs;
  for (const auto machine : machines) {
    std::vector<IntervalVar*> machines_jobs;
    for (int i = 0; i < jobs.size(); ++i) {
      for (int j = 0; j < jobs[i].size(); ++j) {
        if (jobs[i][j].first == machine)
          machines_jobs.push_back(tasks_matrix[i][j]);
      }
    }
    DisjunctiveConstraint* const disj = solver.MakeDisjunctiveConstraint(
        machines_jobs, "Machine_" + std::to_string(machine));
    solver.AddConstraint(disj);
    all_sequences.push_back(disj->MakeSequenceVar());
  }

  // Set the objective.
  std::vector<IntVar*> all_ends;
  for (const auto& job : tasks_matrix) {
    IntervalVar* const task = job.back();
    all_ends.push_back(task->EndExpr()->Var());
  }
  IntVar* const obj_var = solver.MakeMax(all_ends)->Var();
  OptimizeVar* const objective_monitor = solver.MakeMinimize(obj_var, 1);

  // ----- Search monitors and decision builder -----

  // This decision builder will rank all tasks on all machines.
  DecisionBuilder* const sequence_phase =
      solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);

  // After the ranking of tasks, the schedule is still loose and any
  // task can be postponed at will. But, because the problem is now a PERT
  // (http://en.wikipedia.org/wiki/Program_Evaluation_and_Review_Technique),
  // we can schedule each task at its earliest start time. This is
  // conveniently done by fixing the objective variable to its
  // minimum value.
  DecisionBuilder* const obj_phase = solver.MakePhase(
      obj_var, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE);

  // The main decision builder (ranks all tasks, then fixes the
  // objective_variable).
  DecisionBuilder* const main_phase = solver.Compose(sequence_phase, obj_phase);

  // Search log.
  const int kLogFrequency = 1000000;
  SearchMonitor* const search_log =
      solver.MakeSearchLog(kLogFrequency, objective_monitor);

  SearchLimit* limit = nullptr;

  // Create the solution collector.
  SolutionCollector* const collector = solver.MakeLastSolutionCollector();
  collector->Add(all_sequences);
  collector->AddObjective(obj_var);
  for (const auto machine : machines) {
    SequenceVar* const sequence = all_sequences[machine];
    for (int i = 0; i < sequence->size(); ++i) {
      IntervalVar* const t = sequence->Interval(i);
      collector->Add(t->StartExpr()->Var());
      collector->Add(t->EndExpr()->Var());
    }
  }

  // Solve the problem.
  if (solver.Solve(main_phase, search_log, objective_monitor, limit,
                   collector)) {
    LOG(INFO) << "Optimal Schedule Length: " << collector->objective_value(0);
    LOG(INFO) << "";

    LOG(INFO) << "Optimal Schedule:";
    std::vector<std::string> machine_intervals_list;
    for (const auto machine : machines) {
      std::ostringstream machine_tasks;
      SequenceVar* seq = all_sequences[machine];
      machine_tasks << "Machine " << machine << ": ";
      for (const auto s : collector->ForwardSequence(0, seq)) {
        machine_tasks << seq->Interval(s)->name() << " ";
      }
      LOG(INFO) << machine_tasks.str();

      std::ostringstream machine_intervals;
      machine_intervals << "Machine " << machine << ": ";
      for (const auto s : collector->ForwardSequence(0, seq)) {
        IntervalVar* t = seq->Interval(s);
        machine_intervals << "[" << std::setw(2)
                          << collector->Value(0, t->StartExpr()->Var()) << ", "
                          << std::setw(2)
                          << collector->Value(0, t->EndExpr()->Var()) << "] ";
      }
      machine_intervals_list.push_back(machine_intervals.str());
    }
    LOG(INFO) << "Time Intervals for Tasks: ";
    for (const auto& intervals : machine_intervals_list) {
      LOG(INFO) << intervals;
    }
    LOG(INFO) << "Advanced usage:";
    LOG(INFO) << "Time: " << solver.wall_time() << "ms";
  }
}
}  // namespace operations_research

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);
  absl::SetFlag(&FLAGS_logtostderr, 1);
  operations_research::SolveJobShopExample();
  return EXIT_SUCCESS;
}
<commit_msg>fix minimal_jobshop_cp.cc<commit_after>// Copyright 2018 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iomanip>
#include <numeric>  // std::iota

#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"

// Solve a job shop problem:

namespace operations_research {
void SolveJobShopExample() {
  // Instantiate the solver.
  Solver solver("JobShopExample");
  std::array<int, 3> machines;
  std::iota(std::begin(machines), std::end(machines), 0);
  {
    std::ostringstream oss;
    for (auto i : machines) oss << ' ' << i;
    LOG(INFO) << "Machines: " << oss.str();
  }

  // Jobs definition
  using MachineIndex = int;
  using ProcessingTime = int;
  using Task = std::pair<MachineIndex, ProcessingTime>;
  using Job = std::vector<Task>;
  std::vector<Job> jobs = {
      {{0, 3}, {1, 2}, {2, 2}}, {{0, 2}, {2, 1}, {1, 4}}, {{1, 4}, {2, 3}}};
  LOG(INFO) << "Jobs:";
  for (int i = 0; i < jobs.size(); ++i) {
    std::ostringstream problem;
    problem << "Job " << i << ": [";
    for (const Task& task : jobs[i]) {
      problem << "(" << task.first << ", " << task.second << ")";
    }
    problem << "]" << std::endl;
    LOG(INFO) << problem.str();
  }

  // Computes horizon.
  ProcessingTime horizon = 0;
  for (const Job& job : jobs) {
    for (const Task& task : job) {
      horizon += task.second;
    }
  }
  LOG(INFO) << "Horizon: " << horizon;

  // Creates tasks.
  std::vector<std::vector<IntervalVar*>> tasks_matrix(jobs.size());
  for (int i = 0; i < jobs.size(); ++i) {
    for (int j = 0; j < jobs[i].size(); ++j) {
      std::ostringstream oss;
      oss << "Job_" << i << "_" << j;
      tasks_matrix[i].push_back(solver.MakeFixedDurationIntervalVar(
          0, horizon, jobs[i][j].second, false, oss.str()));
    }
  }

  // Add conjunctive contraints.
  for (int i = 0; i < jobs.size(); ++i) {
    for (int j = 0; j < jobs[i].size() - 1; ++j) {
      solver.AddConstraint(solver.MakeIntervalVarRelation(
          tasks_matrix[i][j + 1], Solver::STARTS_AFTER_END,
          tasks_matrix[i][j]));
    }
  }

  // Creates sequence variables and add disjunctive constraints.
  std::vector<SequenceVar*> all_sequences;
  std::vector<IntVar*> all_machines_jobs;
  for (const auto machine : machines) {
    std::vector<IntervalVar*> machines_jobs;
    for (int i = 0; i < jobs.size(); ++i) {
      for (int j = 0; j < jobs[i].size(); ++j) {
        if (jobs[i][j].first == machine)
          machines_jobs.push_back(tasks_matrix[i][j]);
      }
    }
    DisjunctiveConstraint* const disj = solver.MakeDisjunctiveConstraint(
        machines_jobs, "Machine_" + std::to_string(machine));
    solver.AddConstraint(disj);
    all_sequences.push_back(disj->MakeSequenceVar());
  }

  // Set the objective.
  std::vector<IntVar*> all_ends;
  for (const auto& job : tasks_matrix) {
    IntervalVar* const task = job.back();
    all_ends.push_back(task->EndExpr()->Var());
  }
  IntVar* const obj_var = solver.MakeMax(all_ends)->Var();
  OptimizeVar* const objective_monitor = solver.MakeMinimize(obj_var, 1);

  // ----- Search monitors and decision builder -----

  // This decision builder will rank all tasks on all machines.
  DecisionBuilder* const sequence_phase =
      solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);

  // After the ranking of tasks, the schedule is still loose and any
  // task can be postponed at will. But, because the problem is now a PERT
  // (http://en.wikipedia.org/wiki/Program_Evaluation_and_Review_Technique),
  // we can schedule each task at its earliest start time. This is
  // conveniently done by fixing the objective variable to its
  // minimum value.
  DecisionBuilder* const obj_phase = solver.MakePhase(
      obj_var, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE);

  // The main decision builder (ranks all tasks, then fixes the
  // objective_variable).
  DecisionBuilder* const main_phase = solver.Compose(sequence_phase, obj_phase);

  // Search log.
  const int kLogFrequency = 1000000;
  SearchMonitor* const search_log =
      solver.MakeSearchLog(kLogFrequency, objective_monitor);

  SearchLimit* limit = nullptr;

  // Create the solution collector.
  SolutionCollector* const collector = solver.MakeLastSolutionCollector();
  collector->Add(all_sequences);
  collector->AddObjective(obj_var);
  for (const auto machine : machines) {
    SequenceVar* const sequence = all_sequences[machine];
    for (int i = 0; i < sequence->size(); ++i) {
      IntervalVar* const t = sequence->Interval(i);
      collector->Add(t->StartExpr()->Var());
      collector->Add(t->EndExpr()->Var());
    }
  }

  // Solve the problem.
  if (solver.Solve(main_phase, search_log, objective_monitor, limit,
                   collector)) {
    LOG(INFO) << "Optimal Schedule Length: " << collector->objective_value(0);
    LOG(INFO) << "";

    LOG(INFO) << "Optimal Schedule:";
    std::vector<std::string> machine_intervals_list;
    for (const auto machine : machines) {
      std::ostringstream machine_tasks;
      SequenceVar* seq = all_sequences[machine];
      machine_tasks << "Machine " << machine << ": ";
      for (const auto s : collector->ForwardSequence(0, seq)) {
        machine_tasks << seq->Interval(s)->name() << " ";
      }
      LOG(INFO) << machine_tasks.str();

      std::ostringstream machine_intervals;
      machine_intervals << "Machine " << machine << ": ";
      for (const auto s : collector->ForwardSequence(0, seq)) {
        IntervalVar* t = seq->Interval(s);
        machine_intervals << "[(" << std::setw(2)
                          << collector->solution(0)->Min(t->StartExpr()->Var()) << ", "
                          << collector->solution(0)->Max(t->StartExpr()->Var()) << "),("
                          << std::setw(2)
                          << collector->solution(0)->Min(t->EndExpr()->Var()) << ", "
                          << collector->solution(0)->Max(t->EndExpr()->Var()) << ")]";
      }
      machine_intervals_list.push_back(machine_intervals.str());
    }
    LOG(INFO) << "Time Intervals for Tasks: ";
    for (const auto& intervals : machine_intervals_list) {
      LOG(INFO) << intervals;
    }
    LOG(INFO) << "Advanced usage:";
    LOG(INFO) << "Time: " << solver.wall_time() << "ms";
  }
}
}  // namespace operations_research

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);
  absl::SetFlag(&FLAGS_logtostderr, 1);
  operations_research::SolveJobShopExample();
  return EXIT_SUCCESS;
}
<|endoftext|>