Add metrics_dump, a tool to format drm metrics

[ Merge of http://go/wvgerrit/59022 ]

Android metrics are output by the adb shell command
|dumpsys media.metrics|. They appear in bugreports
and can also be requested interactively. Both the
widevine and framework mediadrm metrics are base64
encoded protobufs detailing each of the metrics
items. This tool prints them in a readable format.

Test: wv android unit/integration tests
Change-Id: Id1bc05b34693a3ca44dd3872a28a2337b3ce4d79
This commit is contained in:
Rahul Frias
2019-11-20 14:25:53 -08:00
parent 84061e93d6
commit 8723859570
12 changed files with 1475 additions and 3 deletions

View File

@@ -0,0 +1,201 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Format metric output from |adb shell dumpsys media.metrics|
#include <algorithm>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include "base64decode.h"
#include "mediadrm_metrics.h"
#include "wv_metrics.h"
namespace metrics_dump {
using namespace base64;
using namespace mediadrm_metrics;
using namespace wv_metrics;
using std::cerr;
using std::cout;
using std::endl;
using std::string;
string selected_one;
std::vector<string> excluded_ones;
void process_one_metric(const string &line) {
std::istringstream fields(line);
#define READ_TOKEN(NAME) \
if (!std::getline(fields, NAME, ':')) { \
cerr << "error: expected |" #NAME "| field in '" << line << "'" << endl; \
}
string index, version, component, session, uid, package, package_version;
string pid, finalized, timestamp, item_count;
READ_TOKEN(index);
READ_TOKEN(version);
READ_TOKEN(component);
READ_TOKEN(session);
READ_TOKEN(uid);
READ_TOKEN(package);
READ_TOKEN(package_version);
READ_TOKEN(pid);
READ_TOKEN(finalized);
READ_TOKEN(timestamp);
READ_TOKEN(item_count);
// handle specific package and component selection
if (selected_one.size() && package != selected_one &&
component != selected_one) {
return;
}
if (std::find(excluded_ones.begin(), excluded_ones.end(), package) !=
excluded_ones.end()) {
return;
}
if (std::find(excluded_ones.begin(), excluded_ones.end(), component) !=
excluded_ones.end()) {
return;
}
cout << "===================================================================="
"============="
<< endl;
char timebuf[64];
time_t time_s = stoll(timestamp) / 1000000000LL;
strftime(timebuf, sizeof(timebuf), "%a %b %e %H:%M:%S", localtime(&time_s));
cout << timebuf << " timestamp: " << time_s << endl << endl;
cout << " "
<< "APK PACKAGE -> METRICS COMPONENT" << endl;
cout << " " << package << " -> " << component << " | uid:" << uid
<< " pid:" << pid << endl;
size_t items = std::stoi(item_count);
for (size_t i = 0; i < items; i++) {
string parameter, value;
if (!std::getline(fields, parameter, '=')) {
cerr << "error: expected |parameter| in item " << i << " in '" << line
<< "'" << endl;
}
if (!std::getline(fields, value, ':')) {
cerr << "error: expected |value| in item " << i << " in '" << line << "'"
<< endl;
}
if (parameter == "serialized_metrics") {
string decoded = Base64::Decode(value);
if (component == "drm.vendor.Google.WidevineCDM") {
drm_metrics::WvCdmMetrics metrics;
if (!metrics.ParseFromString(decoded)) {
cerr << "failed to parse proto string" << endl;
} else {
string result;
FormatWvCdmMetrics(metrics, result);
cout << endl << result << endl;
}
} else {
android::drm_metrics::DrmFrameworkMetrics metrics;
if (!metrics.ParseFromString(decoded)) {
cerr << "failed to parse proto string" << endl;
} else {
string result;
FormatDrmFrameworkMetrics(metrics, result);
cout << endl << result << endl;
}
}
} else {
cout << " " << parameter << ": " << value << endl;
}
}
}
void process_metrics(std::istream &instream) {
string line;
while (std::getline(instream, line)) {
if (line.find("serialized_metrics") != string::npos) {
process_one_metric(line);
}
}
}
} // namespace metrics_dump
using std::cerr;
using std::endl;
using std::string;
void usage() {
cerr << "usage: metrics_dump [options] [<bugreport>]" << endl;
cerr << endl;
cerr << "Displays the drm metrics that are generated by" << endl;
cerr << "adb shell dumpsys media.metrics. Input may be from a file" << endl;
cerr << "such as a bugreport, or from stdin." << endl;
cerr << endl;
cerr << "options:" << endl;
cerr << " --no-gms" << endl;
cerr << " ignore metrics from package com.google.android.gms" << endl;
cerr << " which are generated frequently by droidguard" << endl;
cerr << " --widevine" << endl;
cerr << " show only widevine metrics, "
"component=drm.vendor.Google.WidevineCDM"
<< endl;
cerr << " --mediadrm" << endl;
cerr << " show only framework drm metrics, component=mediadrm" << endl;
cerr << " --exclude <package>|<component>" << endl;
cerr << " ignore metrics from the specified package or component"
<< endl;
cerr << " --select <package>|<component>" << endl;
cerr << " only show metrics from the specified package or component"
<< endl;
exit(-1);
}
int main(int argc, char **argv) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
int i = 1;
while (i < argc) {
string arg = argv[i];
if (arg == "--help") {
usage();
} else if (arg == "--no-gms") {
metrics_dump::excluded_ones.push_back("com.google.android.gms");
} else if (arg == "--widevine") {
metrics_dump::selected_one = "drm.vendor.Google.WidevineCDM";
} else if (arg == "--mediadrm") {
metrics_dump::selected_one = "mediadrm";
} else if (i == argc - 1) {
std::ifstream bugfile(argv[i]);
if (bugfile.is_open()) {
metrics_dump::process_metrics(bugfile);
bugfile.close();
} else {
cerr << "unable to open input file " << argv[i] << endl;
}
break;
} else {
// args with a parameter
if (arg == "--exclude") {
metrics_dump::excluded_ones.push_back(argv[++i]);
} else if (arg == "--select") {
metrics_dump::selected_one = argv[++i];
} else {
usage();
}
}
i++;
}
if (i == argc) {
metrics_dump::process_metrics(std::cin);
}
return 0;
}