1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "mojo/public/cpp/bindings/message_dumper.h"
6 
7 #include "base/bind.h"
8 #include "base/files/file.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/no_destructor.h"
13 #include "base/process/process.h"
14 #include "base/rand_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/task/post_task.h"
17 #include "base/task/thread_pool.h"
18 #include "mojo/public/cpp/bindings/message.h"
19 
20 namespace {
21 
DumpDirectory()22 base::FilePath& DumpDirectory() {
23   static base::NoDestructor<base::FilePath> dump_directory;
24   return *dump_directory;
25 }
26 
WriteMessage(uint64_t identifier,const mojo::MessageDumper::MessageEntry & entry)27 void WriteMessage(uint64_t identifier,
28                   const mojo::MessageDumper::MessageEntry& entry) {
29   static uint64_t num = 0;
30 
31   if (!entry.interface_name)
32     return;
33 
34   base::FilePath message_directory =
35       DumpDirectory()
36           .AppendASCII(entry.interface_name)
37           .AppendASCII(base::NumberToString(identifier));
38 
39   if (!base::DirectoryExists(message_directory) &&
40       !base::CreateDirectory(message_directory)) {
41     LOG(ERROR) << "Failed to create" << message_directory.value();
42     return;
43   }
44 
45   std::string filename =
46       base::NumberToString(num++) + "." + entry.method_name + ".mojomsg";
47   base::FilePath path = message_directory.AppendASCII(filename);
48   base::File file(path,
49                   base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
50 
51   file.WriteAtCurrentPos(reinterpret_cast<const char*>(entry.data_bytes.data()),
52                          static_cast<int>(entry.data_bytes.size()));
53 }
54 
55 }  // namespace
56 
57 namespace mojo {
58 
MessageEntry(const uint8_t * data,uint32_t data_size,const char * interface_name,const char * method_name)59 MessageDumper::MessageEntry::MessageEntry(const uint8_t* data,
60                                           uint32_t data_size,
61                                           const char* interface_name,
62                                           const char* method_name)
63     : interface_name(interface_name),
64       method_name(method_name),
65       data_bytes(data, data + data_size) {}
66 
67 MessageDumper::MessageEntry::MessageEntry(const MessageEntry& entry) = default;
68 
~MessageEntry()69 MessageDumper::MessageEntry::~MessageEntry() {}
70 
MessageDumper()71 MessageDumper::MessageDumper() : identifier_(base::RandUint64()) {}
72 
~MessageDumper()73 MessageDumper::~MessageDumper() {}
74 
Accept(mojo::Message * message)75 bool MessageDumper::Accept(mojo::Message* message) {
76   MessageEntry entry(message->data(), message->data_num_bytes(),
77                      message->interface_name(), message->method_name());
78 
79   static base::NoDestructor<scoped_refptr<base::TaskRunner>> task_runner(
80       base::ThreadPool::CreateSequencedTaskRunner(
81           {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
82            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));
83 
84   (*task_runner)
85       ->PostTask(FROM_HERE,
86                  base::BindOnce(&WriteMessage, identifier_, std::move(entry)));
87   return true;
88 }
89 
SetMessageDumpDirectory(const base::FilePath & directory)90 void MessageDumper::SetMessageDumpDirectory(const base::FilePath& directory) {
91   DumpDirectory() = directory;
92 }
93 
GetMessageDumpDirectory()94 const base::FilePath& MessageDumper::GetMessageDumpDirectory() {
95   return DumpDirectory();
96 }
97 
98 }  // namespace mojo
99