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