blob: e068a18ef89bb03dd43c948604f9700e52b0174c [file] [log] [blame]
/*
* 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