| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "uploadthread.h" |
| #include "tuningfork_utils.h" |
| |
| #include <sys/system_properties.h> |
| #include <GLES3/gl32.h> |
| #include <fstream> |
| #include <sstream> |
| #include <cmath> |
| #include "clearcutserializer.h" |
| #include "modp_b64.h" |
| |
| #define LOG_TAG "TuningFork" |
| #include "Log.h" |
| |
| namespace tuningfork { |
| |
| DebugBackend::~DebugBackend() {} |
| |
| TFErrorCode DebugBackend::Process(const ProtobufSerialization &evt_ser) { |
| if (evt_ser.size() == 0) return TFERROR_BAD_PARAMETER; |
| auto encode_len = modp_b64_encode_len(evt_ser.size()); |
| std::vector<char> dest_buf(encode_len); |
| // This fills the dest buffer with a null-terminated string. It returns the length of |
| // the string, not including the null char |
| auto n_encoded = modp_b64_encode(&dest_buf[0], reinterpret_cast<const char*>(&evt_ser[0]), |
| evt_ser.size()); |
| if (n_encoded == -1 || encode_len != n_encoded+1) { |
| ALOGW("Could not b64 encode protobuf"); |
| return TFERROR_B64_ENCODE_FAILED; |
| } |
| std::string s(&dest_buf[0], n_encoded); |
| // Split the serialization into <128-byte chunks to avoid logcat line |
| // truncation. |
| constexpr size_t maxStrLen = 128; |
| int n = (s.size() + maxStrLen - 1) / maxStrLen; // Round up |
| for (int i = 0, j = 0; i < n; ++i) { |
| std::stringstream str; |
| str << "(TCL" << (i + 1) << "/" << n << ")"; |
| int m = std::min(s.size() - j, maxStrLen); |
| str << s.substr(j, m); |
| j += m; |
| ALOGI("%s", str.str().c_str()); |
| } |
| return TFERROR_OK; |
| } |
| |
| std::unique_ptr<DebugBackend> s_debug_backend = std::make_unique<DebugBackend>(); |
| |
| UploadThread::UploadThread(Backend *backend, const ExtraUploadInfo& extraInfo) : backend_(backend), |
| current_fidelity_params_(0), |
| upload_callback_(nullptr), |
| extra_info_(extraInfo) { |
| if (backend_ == nullptr) |
| backend_ = s_debug_backend.get(); |
| Start(); |
| } |
| |
| UploadThread::~UploadThread() { |
| Stop(); |
| } |
| |
| void UploadThread::Start() { |
| if (thread_) { |
| ALOGW("Can't start an already running thread"); |
| return; |
| } |
| do_quit_ = false; |
| ready_ = nullptr; |
| thread_ = std::make_unique<std::thread>([&] { return Run(); }); |
| } |
| |
| void UploadThread::Stop() { |
| if (!thread_->joinable()) { |
| ALOGW("Can't stop a thread that's not started"); |
| return; |
| } |
| do_quit_ = true; |
| cv_.notify_one(); |
| thread_->join(); |
| } |
| |
| void UploadThread::Run() { |
| while (!do_quit_) { |
| std::unique_lock<std::mutex> lock(mutex_); |
| if (ready_) { |
| ProtobufSerialization evt_ser; |
| UpdateGLVersion(); // Needs to be done with an active gl context |
| ClearcutSerializer::SerializeEvent(*ready_, current_fidelity_params_, |
| extra_info_, |
| evt_ser); |
| if(upload_callback_) { |
| CProtobufSerialization cser = { evt_ser.data(), |
| static_cast<uint32_t>(evt_ser.size()), nullptr}; |
| upload_callback_(&cser); |
| } |
| backend_->Process(evt_ser); |
| ready_ = nullptr; |
| } |
| cv_.wait_for(lock, std::chrono::milliseconds(1000)); |
| } |
| } |
| |
| // Returns true if we submitted, false if we are waiting for a previous submit to complete |
| bool UploadThread::Submit(const ProngCache *prongs) { |
| if (ready_ == nullptr) { |
| { |
| std::lock_guard<std::mutex> lock(mutex_); |
| ready_ = prongs; |
| } |
| cv_.notify_one(); |
| return true; |
| } else |
| return false; |
| } |
| |
| namespace { |
| |
| // TODO: replace these with device_info library calls once they are available |
| |
| std::string slurpFile(const char* fname) { |
| std::ifstream f(fname); |
| if (f.good()) { |
| std::stringstream str; |
| str << f.rdbuf(); |
| return str.str(); |
| } |
| return ""; |
| } |
| |
| const char* skipSpace(const char* q) { |
| while(*q && (*q==' ' || *q=='\t')) ++q; |
| return q; |
| } |
| std::string getSystemPropViaGet(const char* key) { |
| char buffer[PROP_VALUE_MAX + 1]=""; // +1 for terminator |
| int bufferLen = __system_property_get(key, buffer); |
| if(bufferLen>0) |
| return buffer; |
| else |
| return ""; |
| } |
| |
| } |
| |
| /* static */ |
| ExtraUploadInfo UploadThread::GetExtraUploadInfo(JNIEnv* env, jobject context) { |
| ExtraUploadInfo extra_info; |
| // Total memory |
| std::string s = slurpFile("/proc/meminfo"); |
| if (!s.empty()) { |
| // Lines like 'MemTotal: 3749460 kB' |
| std::string to_find("MemTotal:"); |
| auto it = s.find(to_find); |
| if(it!=std::string::npos) { |
| const char* p = s.data() + it + to_find.length(); |
| p = skipSpace(p); |
| std::istringstream str(p); |
| uint64_t x; |
| str >> x; |
| std::string units; |
| str >> units; |
| static std::string unitPrefix = "bBkKmMgGtTpP"; |
| auto j = unitPrefix.find(units[0]); |
| uint64_t mult = 1; |
| if (j!=std::string::npos) { |
| mult = ::pow(1024L,j/2); |
| } |
| extra_info.total_memory_bytes = x*mult; |
| } |
| } |
| extra_info.build_version_sdk = getSystemPropViaGet("ro.build.version.sdk"); |
| extra_info.build_fingerprint = getSystemPropViaGet("ro.build.fingerprint"); |
| |
| extra_info.session_id = UniqueId(env); |
| |
| extra_info.cpu_max_freq_hz.clear(); |
| for(int index = 1;;++index) { |
| std::stringstream str; |
| str << "/sys/devices/system/cpu/cpu" << index << "/cpufreq/cpuinfo_max_freq"; |
| auto cpu_freq_file = slurpFile(str.str().c_str()); |
| if (cpu_freq_file.empty()) |
| break; |
| uint64_t freq; |
| std::istringstream cstr(cpu_freq_file); |
| cstr >> freq; |
| extra_info.cpu_max_freq_hz.push_back(freq*1000); // File is in kHz |
| } |
| |
| extra_info.apk_version_code = apk_utils::GetVersionCode(env, context, |
| &extra_info.apk_package_name); |
| |
| extra_info.tuningfork_version = TUNINGFORK_PACKED_VERSION; |
| |
| return extra_info; |
| } |
| |
| void UploadThread::UpdateGLVersion() { |
| // gl_es_version |
| GLint glVerMajor = 2; |
| GLint glVerMinor = 0; |
| glGetIntegerv(GL_MAJOR_VERSION, &glVerMajor); |
| if (glGetError() != GL_NO_ERROR) { |
| glVerMajor = 0; |
| glVerMinor = 0; |
| } else { |
| glGetIntegerv(GL_MINOR_VERSION, &glVerMinor); |
| } |
| extra_info_.gl_es_version = (glVerMajor<<16) + glVerMinor; |
| } |
| |
| } // namespace tuningfork |