chromium/mojo/public/cpp/bindings/lib/message_dumper.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "mojo/public/cpp/bindings/message_dumper.h"

#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/process/process.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/thread_pool.h"
#include "mojo/public/cpp/bindings/message.h"

namespace {

base::FilePath& DumpDirectory() {
  static base::NoDestructor<base::FilePath> dump_directory;
  return *dump_directory;
}

void WriteMessage(uint64_t identifier,
                  const mojo::MessageDumper::MessageEntry& entry) {
  static uint64_t num = 0;

  if (!entry.interface_name)
    return;

  base::FilePath message_directory =
      DumpDirectory()
          .AppendASCII(entry.interface_name)
          .AppendASCII(base::NumberToString(identifier));

  if (!base::DirectoryExists(message_directory) &&
      !base::CreateDirectory(message_directory)) {
    LOG(ERROR) << "Failed to create" << message_directory.value();
    return;
  }

  std::string filename =
      base::NumberToString(num++) + "." + entry.method_name + ".mojomsg";
  base::FilePath path = message_directory.AppendASCII(filename);
  base::File file(path,
                  base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);

  file.WriteAtCurrentPos(entry.data_bytes);
}

}  // namespace

namespace mojo {

MessageDumper::MessageEntry::MessageEntry(const uint8_t* data,
                                          uint32_t data_size,
                                          const char* interface_name,
                                          const char* method_name)
    : interface_name(interface_name),
      method_name(method_name),
      data_bytes(data, data + data_size) {}

MessageDumper::MessageEntry::MessageEntry(const MessageEntry& entry) = default;

MessageDumper::MessageEntry::~MessageEntry() {}

MessageDumper::MessageDumper() : identifier_(base::RandUint64()) {}

MessageDumper::~MessageDumper() {}

bool MessageDumper::Accept(mojo::Message* message) {
  MessageEntry entry(message->data(), message->data_num_bytes(),
                     message->interface_name(), message->method_name());

  static base::NoDestructor<scoped_refptr<base::TaskRunner>> task_runner(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));

  (*task_runner)
      ->PostTask(FROM_HERE,
                 base::BindOnce(&WriteMessage, identifier_, std::move(entry)));
  return true;
}

void MessageDumper::SetMessageDumpDirectory(const base::FilePath& directory) {
  DumpDirectory() = directory;
}

const base::FilePath& MessageDumper::GetMessageDumpDirectory() {
  return DumpDirectory();
}

}  // namespace mojo