Chromium Code Reviews| Index: chrome/browser/chromeos/arc/trace/arc_trace_reader.cc |
| diff --git a/chrome/browser/chromeos/arc/trace/arc_trace_reader.cc b/chrome/browser/chromeos/arc/trace/arc_trace_reader.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ba821d3ff3a33c99e93b47b43c712e1f0b7585c8 |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/arc/trace/arc_trace_reader.cc |
| @@ -0,0 +1,215 @@ |
| +// Copyright 2017 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/arc/trace/arc_trace_reader.h" |
| + |
| +#include <errno.h> |
| +#include <string.h> |
| +#include <sys/select.h> |
| + |
| +#include <memory> |
| +#include <sstream> |
| +#include <utility> |
| +#include <vector> |
| + |
| +#include "base/json/json_writer.h" |
| +#include "base/logging.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/string_split.h" |
| +#include "base/values.h" |
| +#include "content/public/browser/browser_thread.h" |
| + |
| +namespace arc { |
| + |
| +namespace { |
| + |
| +#define ARC_TRACE_MESSAGE_LENGTH (1024 + 512) |
|
Luis Héctor Chávez
2017/02/10 17:43:54
constexpr size_t kArcTraceMessageLength = 1024 + 5
Earl Ou
2017/03/22 11:38:26
Done in Android side.
|
| + |
| +// Maximum data to be stored in the buffer. |
| +constexpr int kTraceBufferByteSize = 16 * 1024 * 1024; // 16MB |
| + |
| +} // namespace |
| + |
| +ArcTraceReader::ArcTraceReader() |
| + : is_recording_(false), input_fd_(-1), recording_thread_(nullptr) {} |
|
Luis Héctor Chávez
2017/02/10 17:43:55
these three can be set in the .h, that way you can
Earl Ou
2017/03/22 11:38:26
Done in Android side.
|
| + |
| +void ArcTraceReader::Run() { |
| + if (input_fd_.get() == -1) { |
| + LOG(WARNING) << "No input FD is set before starting recording."; |
| + return; |
| + } |
| + |
| + int ret; |
| + fd_set fds; |
| + timeval tv; |
| + char buf[ARC_TRACE_MESSAGE_LENGTH]; |
| + |
| + while (is_recording_) { |
| + FD_ZERO(&fds); |
| + FD_SET(input_fd_.get(), &fds); |
| + |
| + // Wait up to 1 second. |
| + tv.tv_sec = 1; |
| + tv.tv_usec = 0; |
| + |
| + ret = TEMP_FAILURE_RETRY( |
| + select(input_fd_.get() + 1, &fds, nullptr, nullptr, &tv)); |
| + if (ret < 0) { |
| + LOG(ERROR) << "Unexpected error while recording ARC trace: " |
| + << strerror(errno); |
| + break; |
| + } |
| + if (!ret) // No data available. |
| + continue; |
| + |
| + ret = TEMP_FAILURE_RETRY(read(input_fd_.get(), buf, sizeof(buf) - 1)); |
|
Luis Héctor Chávez
2017/02/10 23:16:51
in any case, you should try to limit the amount of
Earl Ou
2017/03/22 11:38:26
This is moved to the Android side.
|
| + if (ret < 0) { |
| + LOG(ERROR) << "Unexpected error while reading ARC trace: " |
| + << strerror(errno); |
| + break; |
| + } |
| + if (ret == 0) { // EOF |
| + LOG(WARNING) << "EOF while recording ARC trace."; |
| + break; |
| + } |
| + buf[ret] = 0; // Make sure the string is null terminated. |
| + ParseAndSaveData(buf); |
| + } |
| +} |
| + |
| +void ArcTraceReader::SetInputFD(base::ScopedFD fd) { |
| + input_fd_ = std::move(fd); |
| +} |
| + |
| +void ArcTraceReader::StartRecord() { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + |
| + if (is_recording_) { |
| + LOG(WARNING) << "ARC trace reader already started."; |
| + return; |
| + } |
| + is_recording_ = true; |
| + current_data_size_ = 0; |
| + trace_buffer_ = std::queue<std::string>(); // clean the queue. |
| + |
| + recording_thread_.reset( |
|
Luis Héctor Chávez
2017/02/10 17:43:54
How about using base::FileDescriptorWatcher::Watch
Luis Héctor Chávez
2017/02/10 23:16:51
You can also use something similar to https://cs.c
Earl Ou
2017/03/22 11:38:26
Done in arc_tracing_agent.cc
|
| + new base::DelegateSimpleThread(this, "ArcTraceReader")); |
| + recording_thread_->Start(); |
| +} |
| + |
| +void ArcTraceReader::StopRecord(const StopTracingCallback& callback) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + |
| + if (!is_recording_) { |
| + LOG(WARNING) << "ARC trace reader was not started."; |
| + callback.Run(""); |
| + return; |
| + } |
| + is_recording_ = false; |
| + recording_thread_->Join(); |
| + recording_thread_.reset(nullptr); |
|
Luis Héctor Chávez
2017/02/10 17:43:54
nit: recording_thread_.reset();
Earl Ou
2017/03/22 11:38:26
No reading thread now as suggested.
|
| + |
| + // TODO(shunshingou): run the following in a separated thread to avoid long |
|
Luis Héctor Chávez
2017/02/10 17:43:55
This must be done in this CL, unfortunately. Worst
Luis Héctor Chávez
2017/02/10 17:48:03
D'Oh, this is a socket, not a pipe. Ignore the SIG
Earl Ou
2017/02/16 09:56:45
The comment here is for the processing of stringst
Earl Ou
2017/03/22 11:38:26
Now this is running in IO thread with WatchReadabl
|
| + // processing in UI thread. |
| + std::stringstream ss; |
| + bool is_first = true; |
| + while (!trace_buffer_.empty()) { |
| + if (!is_first) |
| + ss << ","; |
| + is_first = false; |
| + ss << trace_buffer_.front(); |
| + trace_buffer_.pop(); |
| + } |
| + callback.Run(ss.str()); |
| +} |
| + |
| +void ArcTraceReader::WriteTraceEvent(char ph, |
| + int pid, |
| + int tid, |
| + uint64_t ts, |
| + uint64_t tts, |
| + const std::string* name, |
| + const int* count, |
| + const int* id) { |
| + base::DictionaryValue event; |
| + event.SetString("cat", "android"); |
| + event.SetString("ph", std::string(1, ph)); |
| + event.SetInteger("pid", pid); |
| + event.SetInteger("tid", tid); |
| + event.SetDouble("ts", ts); |
| + event.SetDouble("tts", tts); |
| + if (name) |
| + event.SetString("name", *name); |
| + |
| + if (count) |
| + event.SetInteger("args.count", *count); |
| + |
| + if (id) |
| + event.SetInteger("id", *id); |
| + |
| + std::string json; |
| + if (!base::JSONWriter::Write(event, &json)) { |
| + // Bad data, shouldn't happen as the data is prepared by ourself. |
|
Luis Héctor Chávez
2017/02/10 17:43:54
nit: ourselves.
Earl Ou
2017/03/22 11:38:26
Done on the Android side.
|
| + // Skip and return. |
| + return; |
| + } |
| + |
| + current_data_size_ += json.size(); |
|
Luis Héctor Chávez
2017/02/10 17:43:54
Why do you still need this? Why can't you just sen
Luis Héctor Chávez
2017/02/10 23:16:51
Ignore the above comment, since it's the way the A
Earl Ou
2017/02/16 09:56:45
trace_buffer seems to be designed for Chrome traci
Earl Ou
2017/03/22 11:38:26
I'm using TraceBuffer in the new patch now.
|
| + trace_buffer_.emplace(std::move(json)); |
| + |
| + while (current_data_size_ > kTraceBufferByteSize) { |
| + current_data_size_ -= trace_buffer_.front().size(); |
| + trace_buffer_.pop(); |
| + } |
| +} |
| + |
| +void ArcTraceReader::ParseAndSaveData(std::string data) { |
|
Luis Héctor Chávez
2017/02/10 17:43:54
This might fail since |data| might contain multipl
Earl Ou
2017/02/16 09:56:46
The data would only have one trace event since we'
|
| + const std::vector<std::string> tokens = |
| + base::SplitString(data, "|", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); |
| + |
| + // ph|pid|tid|ts|tts |
| + if (tokens.size() < 5 || tokens[0].size() != 1) // bad data, skip. |
| + return; |
| + |
| + char ph = tokens[0][0]; |
| + int pid, tid; |
| + uint64_t ts, tts; |
| + |
| + if (!base::StringToInt(tokens[1], &pid) || |
| + !base::StringToInt(tokens[2], &tid) || |
| + !base::StringToUint64(tokens[3], &ts) || |
| + !base::StringToUint64(tokens[4], &tts)) { |
| + return; // bad data, skip. |
| + } |
| + |
| + switch (ph) { |
| + case 'B': |
| + // B|pid|tid|ts|tts|name |
| + if (tokens.size() != 6) |
| + return; // bad data, skip. |
| + WriteTraceEvent(ph, pid, tid, ts, tts, &tokens[5]); |
| + break; |
| + case 'E': |
| + // E|pid|tid|ts|tts |
| + WriteTraceEvent(ph, pid, tid, ts, tts); |
| + break; |
| + case 'F': |
| + case 'S': |
| + // F|pid|tid|ts|tts|name|value |
| + int id; |
| + if (tokens.size() != 7 || !base::StringToInt(tokens[6], &id)) |
| + return; // bad data, skip. |
| + WriteTraceEvent(ph, pid, tid, ts, tts, &tokens[5], nullptr, &id); |
| + break; |
| + case 'C': |
| + // C|pid|tid|ts|tts|name|value |
| + int count; |
| + if (tokens.size() != 7 || !base::StringToInt(tokens[6], &count)) |
| + return; // bad data, skip. |
| + WriteTraceEvent(ph, pid, tid, ts, tts, &tokens[5], &count); |
| + break; |
| + } |
| +} |
| + |
| +} // namespace arc |