Home | History | Annotate | Download | only in tuningfork
      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