1 // Copyright 2013 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 "content/browser/android/tracing_controller_android.h"
6 
7 #include <string>
8 
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/bind.h"
13 #include "base/json/json_writer.h"
14 #include "base/logging.h"
15 #include "base/trace_event/trace_event.h"
16 #include "content/browser/tracing/tracing_controller_impl.h"
17 #include "content/public/android/content_jni_headers/TracingControllerAndroidImpl_jni.h"
18 #include "content/public/browser/tracing_controller.h"
19 #include "services/tracing/public/cpp/perfetto/perfetto_config.h"
20 #include "services/tracing/public/cpp/perfetto/perfetto_session.h"
21 #include "services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h"
22 #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
23 #include "third_party/perfetto/include/perfetto/tracing/tracing.h"
24 #include "third_party/perfetto/protos/perfetto/common/trace_stats.gen.h"
25 
26 using base::android::JavaParamRef;
27 using base::android::ScopedJavaLocalRef;
28 using base::android::ScopedJavaGlobalRef;
29 
30 namespace content {
31 namespace {
32 
33 // Currently active tracing session.
34 perfetto::TracingSession* g_tracing_session = nullptr;
35 
ReadProtobufTraceData(scoped_refptr<TracingController::TraceDataEndpoint> endpoint,perfetto::TracingSession::ReadTraceCallbackArgs args)36 void ReadProtobufTraceData(
37     scoped_refptr<TracingController::TraceDataEndpoint> endpoint,
38     perfetto::TracingSession::ReadTraceCallbackArgs args) {
39   if (args.size) {
40     auto data_string = std::make_unique<std::string>(args.data, args.size);
41     endpoint->ReceiveTraceChunk(std::move(data_string));
42   }
43   if (!args.has_more)
44     endpoint->ReceivedTraceFinalContents();
45 }
46 
ReadJsonTraceData(scoped_refptr<TracingController::TraceDataEndpoint> endpoint,tracing::TracePacketTokenizer & tokenizer,perfetto::TracingSession::ReadTraceCallbackArgs args)47 void ReadJsonTraceData(
48     scoped_refptr<TracingController::TraceDataEndpoint> endpoint,
49     tracing::TracePacketTokenizer& tokenizer,
50     perfetto::TracingSession::ReadTraceCallbackArgs args) {
51   if (args.size) {
52     auto packets =
53         tokenizer.Parse(reinterpret_cast<const uint8_t*>(args.data), args.size);
54     for (const auto& packet : packets) {
55       for (const auto& slice : packet.slices()) {
56         auto data_string = std::make_unique<std::string>(
57             reinterpret_cast<const char*>(slice.start), slice.size);
58         endpoint->ReceiveTraceChunk(std::move(data_string));
59       }
60     }
61   }
62   if (!args.has_more) {
63     DCHECK(!tokenizer.has_more());
64     endpoint->ReceivedTraceFinalContents();
65   }
66 }
67 
68 }  // namespace
69 
JNI_TracingControllerAndroidImpl_Init(JNIEnv * env,const JavaParamRef<jobject> & obj)70 static jlong JNI_TracingControllerAndroidImpl_Init(
71     JNIEnv* env,
72     const JavaParamRef<jobject>& obj) {
73   TracingControllerAndroid* profiler = new TracingControllerAndroid(env, obj);
74   return reinterpret_cast<intptr_t>(profiler);
75 }
76 
TracingControllerAndroid(JNIEnv * env,jobject obj)77 TracingControllerAndroid::TracingControllerAndroid(JNIEnv* env, jobject obj)
78     : weak_java_object_(env, obj) {}
79 
~TracingControllerAndroid()80 TracingControllerAndroid::~TracingControllerAndroid() {}
81 
Destroy(JNIEnv * env,const JavaParamRef<jobject> & obj)82 void TracingControllerAndroid::Destroy(JNIEnv* env,
83                                        const JavaParamRef<jobject>& obj) {
84   delete this;
85 }
86 
StartTracing(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jstring> & jcategories,const JavaParamRef<jstring> & jtraceoptions,bool use_protobuf)87 bool TracingControllerAndroid::StartTracing(
88     JNIEnv* env,
89     const JavaParamRef<jobject>& obj,
90     const JavaParamRef<jstring>& jcategories,
91     const JavaParamRef<jstring>& jtraceoptions,
92     bool use_protobuf) {
93   std::string categories =
94       base::android::ConvertJavaStringToUTF8(env, jcategories);
95   std::string options =
96       base::android::ConvertJavaStringToUTF8(env, jtraceoptions);
97 
98   // This log is required by adb_profile_chrome.py.
99   LOG(WARNING) << "Logging performance trace to file";
100 
101   base::trace_event::TraceConfig trace_config(categories, options);
102   perfetto::TraceConfig perfetto_config = tracing::GetDefaultPerfettoConfig(
103       base::trace_event::TraceConfig(), /*privacy_filtering_enabled=*/false,
104       /*convert_to_legacy_json=*/!use_protobuf);
105   delete g_tracing_session;
106   g_tracing_session = perfetto::Tracing::NewTrace().release();
107   g_tracing_session->Setup(perfetto_config);
108   g_tracing_session->Start();
109   return true;
110 }
111 
StopTracing(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jstring> & jfilepath,bool compress_file,bool use_protobuf,const base::android::JavaParamRef<jobject> & callback)112 void TracingControllerAndroid::StopTracing(
113     JNIEnv* env,
114     const JavaParamRef<jobject>& obj,
115     const JavaParamRef<jstring>& jfilepath,
116     bool compress_file,
117     bool use_protobuf,
118     const base::android::JavaParamRef<jobject>& callback) {
119   base::FilePath file_path(
120       base::android::ConvertJavaStringToUTF8(env, jfilepath));
121   ScopedJavaGlobalRef<jobject> global_callback(env, callback);
122   auto endpoint = TracingController::CreateFileEndpoint(
123       file_path, base::BindOnce(&TracingControllerAndroid::OnTracingStopped,
124                                 weak_factory_.GetWeakPtr(), global_callback));
125 
126   if (!g_tracing_session) {
127     LOG(ERROR) << "Tried to stop a non-existent tracing session";
128     OnTracingStopped(global_callback);
129     return;
130   }
131 
132   if (compress_file) {
133     endpoint = TracingControllerImpl::CreateCompressedStringEndpoint(
134         endpoint, /*compress_with_background_priority=*/true);
135   }
136 
137   auto session = base::MakeRefCounted<
138       base::RefCountedData<std::unique_ptr<perfetto::TracingSession>>>(
139       base::WrapUnique(g_tracing_session));
140   g_tracing_session = nullptr;
141   if (use_protobuf) {
142     session->data->SetOnStopCallback([session, endpoint] {
143       session->data->ReadTrace(
144           [session,
145            endpoint](perfetto::TracingSession::ReadTraceCallbackArgs args) {
146             ReadProtobufTraceData(endpoint, args);
147           });
148     });
149   } else {
150     auto tokenizer = base::MakeRefCounted<
151         base::RefCountedData<std::unique_ptr<tracing::TracePacketTokenizer>>>(
152         std::make_unique<tracing::TracePacketTokenizer>());
153     session->data->SetOnStopCallback([session, tokenizer, endpoint] {
154       session->data->ReadTrace(
155           [session, tokenizer,
156            endpoint](perfetto::TracingSession::ReadTraceCallbackArgs args) {
157             ReadJsonTraceData(endpoint, *tokenizer->data, args);
158           });
159     });
160   }
161   session->data->Stop();
162 }
163 
GenerateTracingFilePath(base::FilePath * file_path)164 void TracingControllerAndroid::GenerateTracingFilePath(
165     base::FilePath* file_path) {
166   JNIEnv* env = base::android::AttachCurrentThread();
167   ScopedJavaLocalRef<jstring> jfilename =
168       Java_TracingControllerAndroidImpl_generateTracingFilePath(env);
169   *file_path = base::FilePath(
170       base::android::ConvertJavaStringToUTF8(env, jfilename.obj()));
171 }
172 
OnTracingStopped(const base::android::ScopedJavaGlobalRef<jobject> & callback)173 void TracingControllerAndroid::OnTracingStopped(
174     const base::android::ScopedJavaGlobalRef<jobject>& callback) {
175   JNIEnv* env = base::android::AttachCurrentThread();
176   base::android::ScopedJavaLocalRef<jobject> obj = weak_java_object_.get(env);
177   if (obj.obj())
178     Java_TracingControllerAndroidImpl_onTracingStopped(env, obj, callback);
179 }
180 
GetKnownCategoriesAsync(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & callback)181 bool TracingControllerAndroid::GetKnownCategoriesAsync(
182     JNIEnv* env,
183     const JavaParamRef<jobject>& obj,
184     const JavaParamRef<jobject>& callback) {
185   ScopedJavaGlobalRef<jobject> global_callback(env, callback);
186   // TODO(skyostil): Get the categories from Perfetto instead.
187   return TracingController::GetInstance()->GetCategories(
188       base::BindOnce(&TracingControllerAndroid::OnKnownCategoriesReceived,
189                      weak_factory_.GetWeakPtr(), global_callback));
190 }
191 
OnKnownCategoriesReceived(const ScopedJavaGlobalRef<jobject> & callback,const std::set<std::string> & categories_received)192 void TracingControllerAndroid::OnKnownCategoriesReceived(
193     const ScopedJavaGlobalRef<jobject>& callback,
194     const std::set<std::string>& categories_received) {
195   base::ListValue category_list;
196   for (const std::string& category : categories_received)
197     category_list.AppendString(category);
198   std::string received_category_list;
199   base::JSONWriter::Write(category_list, &received_category_list);
200 
201   // This log is required by adb_profile_chrome.py.
202   // TODO(crbug.com/898816): Replace (users of) this with DevTools' Tracing API.
203   LOG(WARNING) << "{\"traceCategoriesList\": " << received_category_list << "}";
204 
205   JNIEnv* env = base::android::AttachCurrentThread();
206   base::android::ScopedJavaLocalRef<jobject> obj = weak_java_object_.get(env);
207   if (obj.obj()) {
208     std::vector<std::string> category_vector(categories_received.begin(),
209                                              categories_received.end());
210     base::android::ScopedJavaLocalRef<jobjectArray> jcategories =
211         base::android::ToJavaArrayOfStrings(env, category_vector);
212     Java_TracingControllerAndroidImpl_onKnownCategoriesReceived(
213         env, obj, jcategories, callback);
214   }
215 }
216 
217 static ScopedJavaLocalRef<jstring>
JNI_TracingControllerAndroidImpl_GetDefaultCategories(JNIEnv * env,const JavaParamRef<jobject> & obj)218 JNI_TracingControllerAndroidImpl_GetDefaultCategories(
219     JNIEnv* env,
220     const JavaParamRef<jobject>& obj) {
221   base::trace_event::TraceConfig trace_config;
222   return base::android::ConvertUTF8ToJavaString(
223       env, trace_config.ToCategoryFilterString());
224 }
225 
GetTraceBufferUsageAsync(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & callback)226 bool TracingControllerAndroid::GetTraceBufferUsageAsync(
227     JNIEnv* env,
228     const JavaParamRef<jobject>& obj,
229     const JavaParamRef<jobject>& callback) {
230   ScopedJavaGlobalRef<jobject> global_callback(env, callback);
231   auto weak_callback =
232       base::BindOnce(&TracingControllerAndroid::OnTraceBufferUsageReceived,
233                      weak_factory_.GetWeakPtr(), global_callback);
234 
235   if (!g_tracing_session) {
236     std::move(weak_callback)
237         .Run(/*percent_full=*/0.f, /*approximate_event_count=*/0);
238     return true;
239   }
240 
241   // |weak_callback| is move-only, so in order to pass it through a copied
242   // lambda we need to temporarily move it on the heap.
243   auto shared_callback = base::MakeRefCounted<
244       base::RefCountedData<base::OnceCallback<void(float, size_t)>>>(
245       std::move(weak_callback));
246   g_tracing_session->GetTraceStats(
247       [shared_callback](
248           perfetto::TracingSession::GetTraceStatsCallbackArgs args) {
249         float percent_full = 0;
250         perfetto::protos::gen::TraceStats trace_stats;
251         if (args.success &&
252             trace_stats.ParseFromArray(args.trace_stats_data.data(),
253                                        args.trace_stats_data.size())) {
254           percent_full = tracing::GetTraceBufferUsage(trace_stats);
255         }
256         // TODO(skyostil): Remove approximate_event_count since no-one is using
257         // it.
258         std::move(shared_callback->data)
259             .Run(percent_full, /*approximate_event_count=*/0);
260       });
261   return true;
262 }
263 
OnTraceBufferUsageReceived(const ScopedJavaGlobalRef<jobject> & callback,float percent_full,size_t approximate_event_count)264 void TracingControllerAndroid::OnTraceBufferUsageReceived(
265     const ScopedJavaGlobalRef<jobject>& callback,
266     float percent_full,
267     size_t approximate_event_count) {
268   JNIEnv* env = base::android::AttachCurrentThread();
269   base::android::ScopedJavaLocalRef<jobject> obj = weak_java_object_.get(env);
270   if (obj.obj()) {
271     Java_TracingControllerAndroidImpl_onTraceBufferUsageReceived(
272         env, obj, percent_full, approximate_event_count, callback);
273   }
274 }
275 
276 }  // namespace content
277