blob: 94e6b757bf9ae5507ece660da6156b5b36127be1 [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 "tuningfork/protobuf_util.h"
#include "ge_serializer.h"
#include "web.h"
#include <sys/system_properties.h>
#include <cstring>
#include <fstream>
#include <sstream>
#include <cmath>
#include "ge_serializer.h"
#include "modp_b64.h"
#define LOG_TAG "TuningFork"
#include "Log.h"
namespace tuningfork {
class DebugBackend : public Backend {
public:
TFErrorCode Process(const std::string& s) override {
if (s.size() == 0) return TFERROR_BAD_PARAMETER;
// 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 << "(TJS" << (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;
}
};
static std::unique_ptr<DebugBackend> s_debug_backend = std::make_unique<DebugBackend>();
void Runnable::Start() {
if (thread_) {
ALOGW("Can't start an already running thread");
return;
}
do_quit_ = false;
thread_ = std::make_unique<std::thread>([&] { Run(); });
}
void Runnable::Run() {
while (!do_quit_) {
std::unique_lock<std::mutex> lock(mutex_);
auto wait_time = DoWork();
cv_.wait_for(lock, wait_time);
}
}
void Runnable::Stop() {
if (!thread_->joinable()) {
ALOGW("Can't stop a thread that's not started");
return;
}
do_quit_ = true;
cv_.notify_one();
thread_->join();
}
UploadThread::UploadThread(Backend *backend, const ExtraUploadInfo& extraInfo) : Runnable(),
backend_(backend),
current_fidelity_params_(0),
upload_callback_(nullptr),
extra_info_(extraInfo),
persister_(nullptr) {
if (backend_ == nullptr)
backend_ = s_debug_backend.get();
Start();
}
UploadThread::~UploadThread() {
Stop();
}
void UploadThread::Start() {
ready_ = nullptr;
Runnable::Start();
}
Duration UploadThread::DoWork() {
if (ready_) {
std::string evt_ser_json;
GESerializer::SerializeEvent(*ready_, current_fidelity_params_,
extra_info_,
evt_ser_json);
if(upload_callback_) {
upload_callback_(evt_ser_json.c_str(), evt_ser_json.size());
}
if (upload_)
backend_->Process(evt_ser_json);
else {
CProtobufSerialization cser;
ToCProtobufSerialization(evt_ser_json, cser);
if (persister_)
persister_->set(HISTOGRAMS_PAUSED, &cser, persister_->user_data);
CProtobufSerialization_Free(&cser);
}
ready_ = nullptr;
}
return std::chrono::seconds(1);
}
// Returns true if we submitted, false if we are waiting for a previous submit to complete
bool UploadThread::Submit(const ProngCache *prongs, bool upload) {
if (ready_ == nullptr) {
{
std::lock_guard<std::mutex> lock(mutex_);
upload_ = upload;
ready_ = prongs;
}
cv_.notify_one();
return true;
} else
return false;
}
namespace {
// TODO(willosborn): 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::BuildExtraUploadInfo() {
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");
if (jni::IsValid())
extra_info.session_id = UniqueId();
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
}
if (jni::IsValid()) {
extra_info.apk_version_code = apk_utils::GetVersionCode(
&extra_info.apk_package_name,
&extra_info.gl_es_version);
}
extra_info.tuningfork_version = TUNINGFORK_PACKED_VERSION;
return extra_info;
}
void UploadThread::InitialChecks(ProngCache& prongs,
IdProvider& id_provider,
const TFCache* persister) {
persister_ = persister;
if (!persister_) {
ALOGE("No persistence mechanism given");
return;
}
// Check for PAUSED prong cache
CProtobufSerialization paused_hists_ser;
if (persister->get(HISTOGRAMS_PAUSED, &paused_hists_ser,
persister_->user_data)==TFERROR_OK) {
std::string paused_hists_str = ToString(paused_hists_ser);
ALOGI("Got PAUSED histograms: %s", paused_hists_str.c_str());
GESerializer::DeserializeAndMerge(paused_hists_str,
id_provider,
prongs);
CProtobufSerialization_Free(&paused_hists_ser);
}
else {
ALOGI("No PAUSED histograms");
}
}
} // namespace tuningfork