| /* |
| * 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 |