1 /*
2 * Copyright (C) 2017 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 #undef LOG_TAG
18 #define LOG_TAG "GraphicsStatsService"
19
20 #include <JankTracker.h>
21 #include <log/log.h>
22 #include <nativehelper/ScopedPrimitiveArray.h>
23 #include <nativehelper/ScopedUtfChars.h>
24 #include <service/GraphicsStatsService.h>
25 #include <stats_event.h>
26 #include <stats_pull_atom_callback.h>
27 #include <statslog_hwui.h>
28
29 #include "GraphicsJNI.h"
30 #include "android/graphics/jni_runtime.h"
31
32 namespace android {
33
34 using namespace android::uirenderer;
35
getAshmemSize(JNIEnv *,jobject)36 static jint getAshmemSize(JNIEnv*, jobject) {
37 return sizeof(ProfileData);
38 }
39
createDump(JNIEnv *,jobject,jint fd,jboolean isProto)40 static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
41 GraphicsStatsService::Dump* dump =
42 GraphicsStatsService::createDump(fd,
43 isProto ? GraphicsStatsService::DumpType::Protobuf
44 : GraphicsStatsService::DumpType::Text);
45 return reinterpret_cast<jlong>(dump);
46 }
47
addToDump(JNIEnv * env,jobject,jlong dumpPtr,jstring jpath,jstring jpackage,jlong versionCode,jlong startTime,jlong endTime,jbyteArray jdata)48 static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
49 jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
50 std::string path;
51 const ProfileData* data = nullptr;
52 LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
53 ScopedByteArrayRO buffer{env};
54 if (jdata != nullptr) {
55 buffer.reset(jdata);
56 LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
57 "Buffer size %zu doesn't match expected %zu!", buffer.size(),
58 sizeof(ProfileData));
59 data = reinterpret_cast<const ProfileData*>(buffer.get());
60 }
61 if (jpath != nullptr) {
62 ScopedUtfChars pathChars(env, jpath);
63 LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(),
64 "Failed to get path chars");
65 path.assign(pathChars.c_str(), pathChars.size());
66 }
67 ScopedUtfChars packageChars(env, jpackage);
68 LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
69 "Failed to get path chars");
70 GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
71 LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
72
73 const std::string package(packageChars.c_str(), packageChars.size());
74 GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
75 }
76
addFileToDump(JNIEnv * env,jobject,jlong dumpPtr,jstring jpath)77 static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
78 ScopedUtfChars pathChars(env, jpath);
79 LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
80 const std::string path(pathChars.c_str(), pathChars.size());
81 GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
82 GraphicsStatsService::addToDump(dump, path);
83 }
84
finishDump(JNIEnv *,jobject,jlong dumpPtr)85 static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
86 GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
87 GraphicsStatsService::finishDump(dump);
88 }
89
finishDumpInMemory(JNIEnv * env,jobject,jlong dumpPtr,jlong pulledData,jboolean lastFullDay)90 static void finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr, jlong pulledData,
91 jboolean lastFullDay) {
92 GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
93 AStatsEventList* data = reinterpret_cast<AStatsEventList*>(pulledData);
94 GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE);
95 }
96
saveBuffer(JNIEnv * env,jobject clazz,jstring jpath,jstring jpackage,jlong versionCode,jlong startTime,jlong endTime,jbyteArray jdata)97 static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
98 jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
99 ScopedByteArrayRO buffer(env, jdata);
100 LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
101 "Buffer size %zu doesn't match expected %zu!", buffer.size(),
102 sizeof(ProfileData));
103 ScopedUtfChars pathChars(env, jpath);
104 LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
105 ScopedUtfChars packageChars(env, jpackage);
106 LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
107 "Failed to get path chars");
108
109 const std::string path(pathChars.c_str(), pathChars.size());
110 const std::string package(packageChars.c_str(), packageChars.size());
111 const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
112 GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
113 }
114
115 static jobject gGraphicsStatsServiceObject = nullptr;
116 static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
117
getJNIEnv()118 static JNIEnv* getJNIEnv() {
119 JavaVM* vm = GraphicsJNI::getJavaVM();
120 JNIEnv* env = nullptr;
121 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
122 if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
123 LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
124 }
125 }
126 return env;
127 }
128
129 // graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
graphicsStatsPullCallback(int32_t atom_tag,AStatsEventList * data,void * cookie)130 static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
131 AStatsEventList* data,
132 void* cookie) {
133 JNIEnv* env = getJNIEnv();
134 if (!env) {
135 return false;
136 }
137 if (gGraphicsStatsServiceObject == nullptr) {
138 ALOGE("Failed to get graphicsstats service");
139 return AStatsManager_PULL_SKIP;
140 }
141
142 for (bool lastFullDay : {true, false}) {
143 env->CallVoidMethod(gGraphicsStatsServiceObject,
144 gGraphicsStatsService_pullGraphicsStatsMethodID,
145 (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE),
146 reinterpret_cast<jlong>(data));
147 if (env->ExceptionCheck()) {
148 env->ExceptionDescribe();
149 env->ExceptionClear();
150 ALOGE("Failed to invoke graphicsstats service");
151 return AStatsManager_PULL_SKIP;
152 }
153 }
154 return AStatsManager_PULL_SUCCESS;
155 }
156
157 // Register a puller for GRAPHICS_STATS atom with the statsd service.
nativeInit(JNIEnv * env,jobject javaObject)158 static void nativeInit(JNIEnv* env, jobject javaObject) {
159 gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
160 AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
161 AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 10); // 10 milliseconds
162 AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 2 * MS_PER_SEC); // 2 seconds
163
164 AStatsManager_setPullAtomCallback(stats::GRAPHICS_STATS, metadata, &graphicsStatsPullCallback,
165 nullptr);
166
167 AStatsManager_PullAtomMetadata_release(metadata);
168 }
169
nativeDestructor(JNIEnv * env,jobject javaObject)170 static void nativeDestructor(JNIEnv* env, jobject javaObject) {
171 AStatsManager_clearPullAtomCallback(stats::GRAPHICS_STATS);
172 env->DeleteGlobalRef(gGraphicsStatsServiceObject);
173 gGraphicsStatsServiceObject = nullptr;
174 }
175
176 } // namespace android
177 using namespace android;
178
179 static const JNINativeMethod sMethods[] =
180 {{"nGetAshmemSize", "()I", (void*)getAshmemSize},
181 {"nCreateDump", "(IZ)J", (void*)createDump},
182 {"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump},
183 {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump},
184 {"nFinishDump", "(J)V", (void*)finishDump},
185 {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory},
186 {"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer},
187 {"nativeInit", "()V", (void*)nativeInit},
188 {"nativeDestructor", "()V", (void*)nativeDestructor}};
189
register_android_graphics_GraphicsStatsService(JNIEnv * env)190 int register_android_graphics_GraphicsStatsService(JNIEnv* env) {
191 jclass graphicsStatsService_class =
192 FindClassOrDie(env, "android/graphics/GraphicsStatsService");
193 gGraphicsStatsService_pullGraphicsStatsMethodID =
194 GetMethodIDOrDie(env, graphicsStatsService_class, "pullGraphicsStats", "(ZJ)V");
195 return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods,
196 NELEM(sMethods));
197 }
198