1 /* 2 * Copyright 2018 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 "uploadthread.h" 18 #include "tuningfork_utils.h" 19 20 #include <sys/system_properties.h> 21 #include <GLES3/gl32.h> 22 #include <fstream> 23 #include <sstream> 24 #include <cmath> 25 #include "clearcutserializer.h" 26 #include "modp_b64.h" 27 28 #define LOG_TAG "TuningFork" 29 #include "Log.h" 30 31 namespace tuningfork { 32 33 DebugBackend::~DebugBackend() {} 34 35 bool DebugBackend::Process(const ProtobufSerialization &evt_ser) { 36 if (evt_ser.size() == 0) return false; 37 auto encode_len = modp_b64_encode_len(evt_ser.size()); 38 std::vector<char> dest_buf(encode_len); 39 // This fills the dest buffer with a null-terminated string. It returns the length of 40 // the string, not including the null char 41 auto n_encoded = modp_b64_encode(&dest_buf[0], reinterpret_cast<const char*>(&evt_ser[0]), 42 evt_ser.size()); 43 if (n_encoded == -1 || encode_len != n_encoded+1) { 44 ALOGW("Could not b64 encode protobuf"); 45 return false; 46 } 47 std::string s(&dest_buf[0], n_encoded); 48 // Split the serialization into <128-byte chunks to avoid logcat line 49 // truncation. 50 constexpr size_t maxStrLen = 128; 51 int n = (s.size() + maxStrLen - 1) / maxStrLen; // Round up 52 for (int i = 0, j = 0; i < n; ++i) { 53 std::stringstream str; 54 str << "(TCL" << (i + 1) << "/" << n << ")"; 55 int m = std::min(s.size() - j, maxStrLen); 56 str << s.substr(j, m); 57 j += m; 58 ALOGI("%s", str.str().c_str()); 59 } 60 return true; 61 } 62 63 std::unique_ptr<DebugBackend> s_debug_backend = std::make_unique<DebugBackend>(); 64 65 UploadThread::UploadThread(Backend *backend, const ExtraUploadInfo& extraInfo) : backend_(backend), 66 current_fidelity_params_(0), 67 upload_callback_(nullptr), 68 extra_info_(extraInfo) { 69 if (backend_ == nullptr) 70 backend_ = s_debug_backend.get(); 71 Start(); 72 } 73 74 UploadThread::~UploadThread() { 75 Stop(); 76 } 77 78 void UploadThread::Start() { 79 if (thread_) { 80 ALOGW("Can't start an already running thread"); 81 return; 82 } 83 do_quit_ = false; 84 ready_ = nullptr; 85 thread_ = std::make_unique<std::thread>([&] { return Run(); }); 86 } 87 88 void UploadThread::Stop() { 89 if (!thread_->joinable()) { 90 ALOGW("Can't stop a thread that's not started"); 91 return; 92 } 93 do_quit_ = true; 94 cv_.notify_one(); 95 thread_->join(); 96 } 97 98 void UploadThread::Run() { 99 while (!do_quit_) { 100 std::unique_lock<std::mutex> lock(mutex_); 101 if (ready_) { 102 ProtobufSerialization evt_ser; 103 UpdateGLVersion(); // Needs to be done with an active gl context 104 ClearcutSerializer::SerializeEvent(*ready_, current_fidelity_params_, 105 extra_info_, 106 evt_ser); 107 if(upload_callback_) { 108 CProtobufSerialization cser = { evt_ser.data(), evt_ser.size(), nullptr}; 109 upload_callback_(&cser); 110 } 111 backend_->Process(evt_ser); 112 ready_ = nullptr; 113 } 114 cv_.wait_for(lock, std::chrono::milliseconds(1000)); 115 } 116 } 117 118 // Returns true if we submitted, false if we are waiting for a previous submit to complete 119 bool UploadThread::Submit(const ProngCache *prongs) { 120 if (ready_ == nullptr) { 121 { 122 std::lock_guard<std::mutex> lock(mutex_); 123 ready_ = prongs; 124 } 125 cv_.notify_one(); 126 return true; 127 } else 128 return false; 129 } 130 131 namespace { 132 133 // TODO: replace these with device_info library calls once they are available 134 135 std::string slurpFile(const char* fname) { 136 std::ifstream f(fname); 137 if (f.good()) { 138 std::stringstream str; 139 str << f.rdbuf(); 140 return str.str(); 141 } 142 return ""; 143 } 144 145 const char* skipSpace(const char* q) { 146 while(*q && (*q==' ' || *q=='\t')) ++q; 147 return q; 148 } 149 std::string getSystemPropViaGet(const char* key) { 150 char buffer[PROP_VALUE_MAX + 1]=""; // +1 for terminator 151 int bufferLen = __system_property_get(key, buffer); 152 if(bufferLen>0) 153 return buffer; 154 else 155 return ""; 156 } 157 158 } 159 160 /* static */ 161 ExtraUploadInfo UploadThread::GetExtraUploadInfo(JNIEnv* env, jobject activity) { 162 ExtraUploadInfo extra_info; 163 // Total memory 164 std::string s = slurpFile("/proc/meminfo"); 165 if (!s.empty()) { 166 // Lines like 'MemTotal: 3749460 kB' 167 std::string to_find("MemTotal:"); 168 auto it = s.find(to_find); 169 if(it!=std::string::npos) { 170 const char* p = s.data() + it + to_find.length(); 171 p = skipSpace(p); 172 std::istringstream str(p); 173 uint64_t x; 174 str >> x; 175 std::string units; 176 str >> units; 177 static std::string unitPrefix = "bBkKmMgGtTpP"; 178 auto j = unitPrefix.find(units[0]); 179 uint64_t mult = 1; 180 if (j!=std::string::npos) { 181 mult = ::pow(1024L,j/2); 182 } 183 extra_info.total_memory_bytes = x*mult; 184 } 185 } 186 extra_info.build_version_sdk = getSystemPropViaGet("ro.build.version.sdk"); 187 extra_info.build_fingerprint = getSystemPropViaGet("ro.build.fingerprint"); 188 189 extra_info.session_id = UniqueId(env); 190 191 extra_info.cpu_max_freq_hz.clear(); 192 for(int index = 1;;++index) { 193 std::stringstream str; 194 str << "/sys/devices/system/cpu/cpu" << index << "/cpufreq/cpuinfo_max_freq"; 195 auto cpu_freq_file = slurpFile(str.str().c_str()); 196 if (cpu_freq_file.empty()) 197 break; 198 uint64_t freq; 199 std::istringstream cstr(cpu_freq_file); 200 cstr >> freq; 201 extra_info.cpu_max_freq_hz.push_back(freq*1000); // File is in kHz 202 } 203 204 extra_info.apk_version_code = apk_utils::GetVersionCode(env, activity, 205 &extra_info.apk_package_name); 206 207 extra_info.tuningfork_version = TUNINGFORK_PACKED_VERSION; 208 209 return extra_info; 210 } 211 212 void UploadThread::UpdateGLVersion() { 213 // gl_es_version 214 GLint glVerMajor = 2; 215 GLint glVerMinor = 0; 216 glGetIntegerv(GL_MAJOR_VERSION, &glVerMajor); 217 if (glGetError() != GL_NO_ERROR) { 218 glVerMajor = 0; 219 glVerMinor = 0; 220 } else { 221 glGetIntegerv(GL_MINOR_VERSION, &glVerMinor); 222 } 223 extra_info_.gl_es_version = (glVerMajor<<16) + glVerMinor; 224 } 225 226 } // namespace tuningfork 227