1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "TraceBuffer.h"
18
19 #include <chrono>
20 #include <sstream>
21 #include <unistd.h>
22 #include <vector>
23
24 #include <inttypes.h>
25
26 #include "android-base/utf8.h"
27
28 #include "util/Files.h"
29
30 namespace aapt {
31 namespace tracebuffer {
32
33 namespace {
34
35 constexpr char kBegin = 'B';
36 constexpr char kEnd = 'E';
37
38 struct TracePoint {
39 char type;
40 pid_t tid;
41 int64_t time;
42 std::string tag;
43 };
44
45 std::vector<TracePoint> traces;
46 bool enabled = true;
47 constinit std::chrono::steady_clock::time_point startTime = {};
48
GetTime()49 int64_t GetTime() noexcept {
50 auto now = std::chrono::steady_clock::now();
51 if (startTime == decltype(tracebuffer::startTime){}) {
52 startTime = now;
53 }
54 return std::chrono::duration_cast<std::chrono::microseconds>(now - startTime).count();
55 }
56
AddWithTime(std::string tag,char type,int64_t time)57 void AddWithTime(std::string tag, char type, int64_t time) noexcept {
58 TracePoint t = {type, getpid(), time, std::move(tag)};
59 traces.emplace_back(std::move(t));
60 }
61
Add(std::string tag,char type)62 void Add(std::string tag, char type) noexcept {
63 AddWithTime(std::move(tag), type, GetTime());
64 }
65
Flush(const std::string & basePath)66 void Flush(const std::string& basePath) {
67 if (basePath.empty()) {
68 return;
69 }
70 BeginTrace(__func__); // We can't do much here, only record that it happened.
71
72 std::ostringstream s;
73 s << basePath << aapt::file::sDirSep << "report_aapt2_" << getpid() << ".json";
74 FILE* f = android::base::utf8::fopen(s.str().c_str(), "a");
75 if (f == nullptr) {
76 return;
77 }
78
79 // Wrap the trace in a JSON array [] to make Chrome/Perfetto UI handle it.
80 char delimiter = '[';
81 for (const TracePoint& trace : traces) {
82 fprintf(f,
83 "%c{\"ts\" : \"%" PRIu64
84 "\", \"ph\" : \"%c\", \"tid\" : \"%d\" , \"pid\" : \"%d\", \"name\" : \"%s\" }\n",
85 delimiter, trace.time, trace.type, 0, trace.tid, trace.tag.c_str());
86 delimiter = ',';
87 }
88 if (!traces.empty()) {
89 fprintf(f, "]");
90 }
91 fclose(f);
92 traces.clear();
93 }
94
95 } // namespace
96
97 } // namespace tracebuffer
98
BeginTrace(std::string tag)99 void BeginTrace(std::string tag) {
100 if (!tracebuffer::enabled) return;
101 tracebuffer::Add(std::move(tag), tracebuffer::kBegin);
102 }
103
EndTrace(std::string tag)104 void EndTrace(std::string tag) {
105 if (!tracebuffer::enabled) return;
106 tracebuffer::Add(std::move(tag), tracebuffer::kEnd);
107 }
108
enable(bool value)109 bool Trace::enable(bool value) {
110 return tracebuffer::enabled = value;
111 }
112
Trace(const char * tag)113 Trace::Trace(const char* tag) {
114 if (!tracebuffer::enabled) return;
115 tag_.assign(tag);
116 tracebuffer::Add(tag_, tracebuffer::kBegin);
117 }
118
Trace(std::string tag)119 Trace::Trace(std::string tag) : tag_(std::move(tag)) {
120 if (!tracebuffer::enabled) return;
121 tracebuffer::Add(tag_, tracebuffer::kBegin);
122 }
123
124 template <class SpanOfStrings>
makeTag(std::string_view tag,const SpanOfStrings & args)125 std::string makeTag(std::string_view tag, const SpanOfStrings& args) {
126 std::ostringstream s;
127 s << tag;
128 if (!args.empty()) {
129 for (const auto& arg : args) {
130 s << ' ';
131 s << arg;
132 }
133 }
134 return std::move(s).str();
135 }
136
Trace(std::string_view tag,const std::vector<android::StringPiece> & args)137 Trace::Trace(std::string_view tag, const std::vector<android::StringPiece>& args) {
138 if (!tracebuffer::enabled) return;
139 tag_ = makeTag(tag, args);
140 tracebuffer::Add(tag_, tracebuffer::kBegin);
141 }
142
~Trace()143 Trace::~Trace() {
144 if (!tracebuffer::enabled) return;
145 tracebuffer::Add(std::move(tag_), tracebuffer::kEnd);
146 }
147
FlushTrace(std::string_view basepath,std::string_view tag)148 FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag) {
149 if (!Trace::enable(!basepath.empty())) return;
150 basepath_.assign(basepath);
151 tag_.assign(tag);
152 tracebuffer::Add(tag_, tracebuffer::kBegin);
153 }
154
FlushTrace(std::string_view basepath,std::string_view tag,const std::vector<android::StringPiece> & args)155 FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag,
156 const std::vector<android::StringPiece>& args) {
157 if (!Trace::enable(!basepath.empty())) return;
158 basepath_.assign(basepath);
159 tag_ = makeTag(tag, args);
160 tracebuffer::Add(tag_, tracebuffer::kBegin);
161 }
162
FlushTrace(std::string_view basepath,std::string_view tag,const std::vector<std::string> & args)163 FlushTrace::FlushTrace(std::string_view basepath, std::string_view tag,
164 const std::vector<std::string>& args) {
165 if (!Trace::enable(!basepath.empty())) return;
166 basepath_.assign(basepath);
167 tag_ = makeTag(tag, args);
168 tracebuffer::Add(tag_, tracebuffer::kBegin);
169 }
170
~FlushTrace()171 FlushTrace::~FlushTrace() {
172 if (!tracebuffer::enabled) return;
173 tracebuffer::Add(std::move(tag_), tracebuffer::kEnd);
174 tracebuffer::Flush(basepath_);
175 }
176
177 } // namespace aapt
178