From 87238595705418f8410f0e465b9e10faf2595264 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 20 Nov 2019 14:25:53 -0800 Subject: [PATCH] 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 --- .../cdm/core/test/test_printers.cpp | 182 +++++++- libwvdrmengine/cdm/core/test/test_printers.h | 2 + libwvdrmengine/tools/metrics_dump/Makefile | 38 ++ .../tools/metrics_dump/include/base64decode.h | 19 + .../metrics_dump/include/mediadrm_metrics.h | 15 + .../tools/metrics_dump/include/wv_metrics.h | 14 + .../metrics_dump/proto/mediadrm_metrics.proto | 111 +++++ .../tools/metrics_dump/proto/wv_metrics.proto | 219 +++++++++ .../tools/metrics_dump/src/base64decode.cpp | 89 ++++ .../metrics_dump/src/mediadrm_metrics.cpp | 173 ++++++++ .../tools/metrics_dump/src/metrics_dump.cpp | 201 +++++++++ .../tools/metrics_dump/src/wv_metrics.cpp | 415 ++++++++++++++++++ 12 files changed, 1475 insertions(+), 3 deletions(-) create mode 100644 libwvdrmengine/tools/metrics_dump/Makefile create mode 100644 libwvdrmengine/tools/metrics_dump/include/base64decode.h create mode 100644 libwvdrmengine/tools/metrics_dump/include/mediadrm_metrics.h create mode 100644 libwvdrmengine/tools/metrics_dump/include/wv_metrics.h create mode 100644 libwvdrmengine/tools/metrics_dump/proto/mediadrm_metrics.proto create mode 100644 libwvdrmengine/tools/metrics_dump/proto/wv_metrics.proto create mode 100644 libwvdrmengine/tools/metrics_dump/src/base64decode.cpp create mode 100644 libwvdrmengine/tools/metrics_dump/src/mediadrm_metrics.cpp create mode 100644 libwvdrmengine/tools/metrics_dump/src/metrics_dump.cpp create mode 100644 libwvdrmengine/tools/metrics_dump/src/wv_metrics.cpp diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 4e09a8d7..d703a93d 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -941,7 +941,7 @@ void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) { *os << "Unknown CdmLicenseType"; break; } -}; +} void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) { switch (value) { @@ -964,7 +964,7 @@ void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) { *os << "Unknown CdmSecurityLevel"; break; } -}; +} void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) { switch (value) { @@ -978,6 +978,182 @@ void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) { *os << "Unknown CdmCertificateType"; break; } -}; +} + +void PrintTo(const enum OEMCryptoResult& value, ::std::ostream* os) { + switch (value) { + case OEMCrypto_SUCCESS: + *os << "SUCCESS"; + break; + case OEMCrypto_ERROR_INIT_FAILED: + *os << "INIT_FAILED"; + break; + case OEMCrypto_ERROR_TERMINATE_FAILED: + *os << "TERMINATE_FAILED"; + break; + case OEMCrypto_ERROR_OPEN_FAILURE: + *os << "OPEN_FAILURE"; + break; + case OEMCrypto_ERROR_CLOSE_FAILURE: + *os << "CLOSE_FAILURE"; + break; + case OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED: + *os << "ENTER_SECURE_PLAYBACK_FAILED"; + break; + case OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED: + *os << "EXIT_SECURE_PLAYBACK_FAILED"; + break; + case OEMCrypto_ERROR_SHORT_BUFFER: + *os << "SHORT_BUFFER"; + break; + case OEMCrypto_ERROR_NO_DEVICE_KEY: + *os << "NO_DEVICE_KEY"; + break; + case OEMCrypto_ERROR_NO_ASSET_KEY: + *os << "NO_ASSET_KEY"; + break; + case OEMCrypto_ERROR_KEYBOX_INVALID: + *os << "KEYBOX_INVALID"; + break; + case OEMCrypto_ERROR_NO_KEYDATA: + *os << "NO_KEYDATA"; + break; + case OEMCrypto_ERROR_NO_CW: + *os << "NO_CW"; + break; + case OEMCrypto_ERROR_DECRYPT_FAILED: + *os << "DECRYPT_FAILED"; + break; + case OEMCrypto_ERROR_WRITE_KEYBOX: + *os << "WRITE_KEYBOX"; + break; + case OEMCrypto_ERROR_WRAP_KEYBOX: + *os << "WRAP_KEYBOX"; + break; + case OEMCrypto_ERROR_BAD_MAGIC: + *os << "BAD_MAGIC"; + break; + case OEMCrypto_ERROR_BAD_CRC: + *os << "BAD_CRC"; + break; + case OEMCrypto_ERROR_NO_DEVICEID: + *os << "NO_DEVICEID"; + break; + case OEMCrypto_ERROR_RNG_FAILED: + *os << "RNG_FAILED"; + break; + case OEMCrypto_ERROR_RNG_NOT_SUPPORTED: + *os << "RNG_NOT_SUPPORTED"; + break; + case OEMCrypto_ERROR_SETUP: + *os << "SETUP"; + break; + case OEMCrypto_ERROR_OPEN_SESSION_FAILED: + *os << "OPEN_SESSION_FAILED"; + break; + case OEMCrypto_ERROR_CLOSE_SESSION_FAILED: + *os << "CLOSE_SESSION_FAILED"; + break; + case OEMCrypto_ERROR_INVALID_SESSION: + *os << "INVALID_SESSION"; + break; + case OEMCrypto_ERROR_NOT_IMPLEMENTED: + *os << "NOT_IMPLEMENTED"; + break; + case OEMCrypto_ERROR_NO_CONTENT_KEY: + *os << "NO_CONTENT_KEY"; + break; + case OEMCrypto_ERROR_CONTROL_INVALID: + *os << "CONTROL_INVALID"; + break; + case OEMCrypto_ERROR_UNKNOWN_FAILURE: + *os << "UNKNOWN_FAILURE"; + break; + case OEMCrypto_ERROR_INVALID_CONTEXT: + *os << "INVALID_CONTEXT"; + break; + case OEMCrypto_ERROR_SIGNATURE_FAILURE: + *os << "SIGNATURE_FAILURE"; + break; + case OEMCrypto_ERROR_TOO_MANY_SESSIONS: + *os << "TOO_MANY_SESSIONS"; + break; + case OEMCrypto_ERROR_INVALID_NONCE: + *os << "INVALID_NONCE"; + break; + case OEMCrypto_ERROR_TOO_MANY_KEYS: + *os << "TOO_MANY_KEYS"; + break; + case OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED: + *os << "DEVICE_NOT_RSA_PROVISIONED"; + break; + case OEMCrypto_ERROR_INVALID_RSA_KEY: + *os << "INVALID_RSA_KEY"; + break; + case OEMCrypto_ERROR_KEY_EXPIRED: + *os << "KEY_EXPIRED"; + break; + case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES: + *os << "INSUFFICIENT_RESOURCES"; + break; + case OEMCrypto_ERROR_INSUFFICIENT_HDCP: + *os << "INSUFFICIENT_HDCP"; + break; + case OEMCrypto_ERROR_BUFFER_TOO_LARGE: + *os << "BUFFER_TOO_LARGE"; + break; + case OEMCrypto_WARNING_GENERATION_SKEW: + *os << "OEMCrypto_WARNING_GENERATION_SKEW"; + break; + case OEMCrypto_ERROR_GENERATION_SKEW: + *os << "GENERATION_SKEW"; + break; + case OEMCrypto_LOCAL_DISPLAY_ONLY: + *os << "OEMCrypto_LOCAL_DISPLAY_ONLY"; + break; + case OEMCrypto_ERROR_ANALOG_OUTPUT: + *os << "ANALOG_OUTPUT"; + break; + case OEMCrypto_ERROR_WRONG_PST: + *os << "WRONG_PST"; + break; + case OEMCrypto_ERROR_WRONG_KEYS: + *os << "WRONG_KEYS"; + break; + case OEMCrypto_ERROR_MISSING_MASTER: + *os << "MISSING_MASTER"; + break; + case OEMCrypto_ERROR_LICENSE_INACTIVE: + *os << "LICENSE_INACTIVE"; + break; + case OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE: + *os << "ENTRY_NEEDS_UPDATE"; + break; + case OEMCrypto_ERROR_ENTRY_IN_USE: + *os << "ENTRY_IN_USE"; + break; + case OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE: + *os << "USAGE_TABLE_UNRECOVERABLE"; + break; + case OEMCrypto_KEY_NOT_LOADED: + *os << "OEMCrypto_KEY_NOT_LOADED"; + break; + case OEMCrypto_KEY_NOT_ENTITLED: + *os << "OEMCrypto_KEY_NOT_ENTITLED"; + break; + case OEMCrypto_ERROR_BAD_HASH: + *os << "BAD_HASH"; + break; + case OEMCrypto_ERROR_OUTPUT_TOO_LARGE: + *os << "OUTPUT_TOO_LARGE"; + break; + case OEMCrypto_ERROR_SESSION_LOST_STATE: + *os << "SESSION_LOST_STATE"; + break; + case OEMCrypto_ERROR_SYSTEM_INVALIDATED: + *os << "SYSTEM_INVALIDATED"; + break; + } +} } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/test_printers.h b/libwvdrmengine/cdm/core/test/test_printers.h index b41788f1..42bed79c 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.h +++ b/libwvdrmengine/cdm/core/test/test_printers.h @@ -8,6 +8,7 @@ #define CDM_TEST_PRINTERS_H_ #include +#include "OEMCryptoCENC.h" #include "wv_cdm_types.h" namespace wvcdm { @@ -15,6 +16,7 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os); void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os); void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os); void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os); +void PrintTo(const enum OEMCryptoResult& value, ::std::ostream* os); } // namespace wvcdm #endif // CDM_TEST_PRINTERS_H_ diff --git a/libwvdrmengine/tools/metrics_dump/Makefile b/libwvdrmengine/tools/metrics_dump/Makefile new file mode 100644 index 00000000..120534a7 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/Makefile @@ -0,0 +1,38 @@ +CDM=../../../ +OEMCRYPTO=$(CDM)/oemcrypto/include +METRICS=$(CDM)/metrics + +CFLAGS = -g -Wall -Iinclude -Igen `pkg-config --cflags protobuf` \ + -I$(OEMCRYPTO) -I$(CDM)/core/include -I$(CDM)/core/test + +OBJS := obj/metrics_dump.o obj/base64decode.o \ + obj/mediadrm_metrics.pb.o obj/mediadrm_metrics.o \ + obj/wv_metrics.o obj/metrics.pb.o obj/test_printers.o + +PROTO_HEADERS := gen/mediadrm_metrics.pb.h gen/metrics.pb.h + +bin/metrics_dump: $(OBJS) | bin + echo "objs=" $(OBJS) + g++ $(CFLAGS) -o $@ $(OBJS) `pkg-config --libs protobuf` + +gen/%.pb.cc gen/%.pb.h: $(METRICS)/src/%.proto | gen + protoc -I$(METRICS)/src/ --cpp_out=gen $< + +gen/%.pb.cc gen/%.pb.h: proto/%.proto | gen + protoc -Iproto --cpp_out=gen $< + +obj/%.o: src/%.cpp $(PROTO_HEADERS) | obj + g++ -c $(CFLAGS) $< -o $@ + +obj/%.o: gen/%.cc $(PROTO_HEADERS) | obj + g++ -c $(CFLAGS) $< -o $@ + +obj/%.o: $(CDM)/core/test/%.cpp | obj + g++ -c $(CFLAGS) $< -o $@ + +obj gen bin: + mkdir $@ + +clean: + rm -rf obj bin gen + diff --git a/libwvdrmengine/tools/metrics_dump/include/base64decode.h b/libwvdrmengine/tools/metrics_dump/include/base64decode.h new file mode 100644 index 00000000..0918cf7b --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/include/base64decode.h @@ -0,0 +1,19 @@ +// 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. +// +// simple base64 decoder + +#include + +namespace base64 { + +class Base64 { + public: + Base64() {} + ~Base64() {} + + static std::string Decode(const std::string &s); +}; + +} // namespace base64 diff --git a/libwvdrmengine/tools/metrics_dump/include/mediadrm_metrics.h b/libwvdrmengine/tools/metrics_dump/include/mediadrm_metrics.h new file mode 100644 index 00000000..37770cb7 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/include/mediadrm_metrics.h @@ -0,0 +1,15 @@ +// 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 mediadrm framework protobuf metrics + +#include + +#include "mediadrm_metrics.pb.h" + +namespace mediadrm_metrics { +void FormatDrmFrameworkMetrics( + const android::drm_metrics::DrmFrameworkMetrics& metrics, + std::string& result); +}; diff --git a/libwvdrmengine/tools/metrics_dump/include/wv_metrics.h b/libwvdrmengine/tools/metrics_dump/include/wv_metrics.h new file mode 100644 index 00000000..740affa1 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/include/wv_metrics.h @@ -0,0 +1,14 @@ +// 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 widevine protobuf metrics + +#include + +#include "metrics.pb.h" + +namespace wv_metrics { +void FormatWvCdmMetrics(const drm_metrics::WvCdmMetrics& metrics, + std::string& result); +}; diff --git a/libwvdrmengine/tools/metrics_dump/proto/mediadrm_metrics.proto b/libwvdrmengine/tools/metrics_dump/proto/mediadrm_metrics.proto new file mode 100644 index 00000000..11728d34 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/proto/mediadrm_metrics.proto @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 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. + */ + +syntax = "proto2"; + +package android.drm_metrics; + +// need this if we are using libprotobuf-cpp-2.3.0-lite +option optimize_for = LITE_RUNTIME; + + +// This message contains the specific metrics captured by DrmMetrics. It is +// used for serializing and logging metrics. +// next id: 11. +message DrmFrameworkMetrics { + // TODO: Consider using extensions. + + // Attributes are associated with a recorded value. E.g. A counter may + // represent a count of an operation returning a specific error code. The + // error code will be an attribute. + message Attributes { + // Reserved for compatibility with logging proto. + reserved 2 to 13; + + // A general purpose error code where 0 means OK. + optional int32 error_code = 1; + + // Defined at ::android::hardware::drm::V1_0::KeyStatusType; + optional uint32 key_status_type = 14; + + // Defined at ::android::hardware::drm::V1_0::EventType; + optional uint32 event_type = 15; + } + + // The Counter message is used to store a count value with an associated + // Attribute. + message Counter { + optional uint64 count = 1; + // Represents the attributes associated with this counter instance. + optional Attributes attributes = 2; + } + + // The DistributionMetric is meant to capture the moments of a normally + // distributed (or approximately normal) value. + message DistributionMetric { + optional float min = 1; + optional float max = 2; + optional float mean = 3; + optional double variance = 4; + optional uint64 operation_count = 5; + + // Represents the attributes assocated with this distribution metric + // instance. + optional Attributes attributes = 6; + } + + message SessionLifetime { + // Start time of the session in milliseconds since epoch. + optional uint64 start_time_ms = 1; + + // End time of the session in milliseconds since epoch. + optional uint64 end_time_ms = 2; + } + + // The count of open session operations. Each instance has a specific error + // code associated with it. + repeated Counter open_session_counter = 1; + + // The count of close session operations. Each instance has a specific error + // code associated with it. + repeated Counter close_session_counter = 2; + + // Count and execution time of getKeyRequest calls. + repeated DistributionMetric get_key_request_time_us = 3; + + // Count and execution time of provideKeyResponse calls. + repeated DistributionMetric provide_key_response_time_us = 4; + + // Count of getProvisionRequest calls. + repeated Counter get_provisioning_request_counter = 5; + + // Count of provideProvisionResponse calls. + repeated Counter provide_provisioning_response_counter = 6; + + // Count of key status events broken out by status type. + repeated Counter key_status_change_counter = 7; + + // Count of events broken out by event type + repeated Counter event_callback_counter = 8; + + // Count getPropertyByteArray calls to retrieve the device unique id. + repeated Counter get_device_unique_id_counter = 9; + + // Session ids to lifetime (start and end time) map. + // Session ids are strings of hex-encoded byte arrays. + map session_lifetimes = 10; +} + diff --git a/libwvdrmengine/tools/metrics_dump/proto/wv_metrics.proto b/libwvdrmengine/tools/metrics_dump/proto/wv_metrics.proto new file mode 100644 index 00000000..680d35e6 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/proto/wv_metrics.proto @@ -0,0 +1,219 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains a proto definition for serialization of metrics data. +// +syntax = "proto2"; + +package drm_metrics; + +// need this if we are using libprotobuf-cpp-2.3.0-lite +option optimize_for = LITE_RUNTIME; + +// The Attributes message is used to contain values associated with the +// metric that was captured. E.g. if we're capturing a counter value, the +// Attributes will contain the values assocated with that counter. E.g. we may +// want to count all of the operations with a give error code. +message Attributes { + // Reserved for compatibility with logging proto. + // TODO(blueeyes): The reserved keyword is not supported in the older version + // of protoc in the CE CDM third_party directory. Uncomment the reserved + // line when we upgrade. b/67016366. + // reserved 8, 10 to 13; + + // The error code. See CdmResponseType in wv_cdm_types.h + optional int32 error_code = 1; + // The status of the processed data. Some methods has a bool error code + // rather than a CdmResponseType error code. True if it succeeds, and + // false if it fails. + optional bool error_code_bool = 2; + // The CdmSecurityLevel. Defined in wv_cdm_types.h + optional uint32 cdm_security_level = 3; + // The SecurityLevel. Defined in wv_cdm_types.h. + optional uint32 security_level = 4; + // The length in bytes. + optional uint64 length = 5; + // The CDM encryption algorithm. It can be "AES-CBC-128" or unknown. See + // CdmEncryptionAlgorithm in wv_cdm_types.h + optional uint32 encryption_algorithm = 6; + // The CDM signing algorithm. It can be "HMACSHA256" or unknown. See + // CdmSigningAlgorithm in wv_cdm_types.h + optional uint32 signing_algorithm = 7; + // The OEM crypto result. See OEMCryptoResult in OEMCryptoCENC.h + optional int32 oem_crypto_result = 9; + // Defined at ::android::hardware::drm::V1_0::KeyStatusType; + optional uint32 key_status_type = 14; + // Defined at ::android::hardware::drm::V1_0::EventType; + optional uint32 event_type = 15; + // Contains the CdmKeyRequestType defined in wv_cdm_types.h. + optional uint32 key_request_type = 16; +} + +// The Counter message is used to store a count value with an associated +// Attribute. +message CounterMetric { + optional int64 count = 1; + // Represents the attributes associated with this counter instance. + optional Attributes attributes = 2; +} + +// The DistributionMetric is meant to capture the moments of a normally +// distributed (or approximately normal) value. +message DistributionMetric { + optional float min = 1; + optional float max = 2; + optional float mean = 3; + optional double variance = 4; + optional uint64 operation_count = 5; + + // Represents the attributes assocated with this distribution metric + // instance. + optional Attributes attributes = 6; +} + +// ValueMetric represents either a single value or an error. +message ValueMetric { + // Only one of the following values should be set for the given metric. + optional int32 error_code = 1; + optional int64 int_value = 2; + optional double double_value = 3; + optional string string_value = 4; +} + + +// This message contains the specific metrics captured by DrmMetrics. It is +// used for serializing and logging metrics. +// next id: 3. +message WvCdmMetrics { + // Attributes are associated with a recorded value. E.g. A counter may + // represent a count of an operation returning a specific error code. The + // error code will be an attribute. + + // This contains metrics that were captured at the CryptoSession level. These + // include CryptoSession metrics and most OEMCrypto metrics. + // next id: 58 + message CryptoMetrics { + // Crypto Session Metrics. + optional ValueMetric crypto_session_security_level = 1; + repeated CounterMetric crypto_session_delete_all_usage_reports = 2; + repeated CounterMetric crypto_session_delete_multiple_usage_information = 3; + repeated DistributionMetric crypto_session_generic_decrypt_time_us = 4; + repeated DistributionMetric crypto_session_generic_encrypt_time_us = 5; + repeated DistributionMetric crypto_session_generic_sign_time_us = 6; + repeated DistributionMetric crypto_session_generic_verify_time_us = 7; + repeated CounterMetric crypto_session_get_device_unique_id = 8; + repeated CounterMetric crypto_session_get_token = 9; + optional ValueMetric crypto_session_life_span = 10; + repeated DistributionMetric crypto_session_load_certificate_private_key_time_us = 11; + repeated DistributionMetric crypto_session_open_time_us = 12; + optional ValueMetric crypto_session_system_id = 13; + repeated DistributionMetric crypto_session_update_usage_information_time_us = 14; + repeated DistributionMetric crypto_session_update_usage_entry_time_us = 56; + optional ValueMetric crypto_session_usage_information_support = 15; + // OemCrypto metrics. + optional ValueMetric oemcrypto_api_version = 16; + repeated CounterMetric oemcrypto_close_session = 17; + repeated DistributionMetric oemcrypto_copy_buffer_time_us = 18; + optional ValueMetric oemcrypto_current_hdcp_capability = 19; + repeated CounterMetric oemcrypto_deactivate_usage_entry = 20; + repeated DistributionMetric oemcrypto_decrypt_cenc_time_us = 21; + repeated CounterMetric oemcrypto_delete_usage_entry = 22; + repeated CounterMetric oemcrypto_delete_usage_table = 23; + repeated DistributionMetric oemcrypto_derive_keys_from_session_key_time_us = 24; + repeated CounterMetric oemcrypto_force_delete_usage_entry = 25; + repeated DistributionMetric oemcrypto_generate_derived_keys_time_us = 26; + repeated CounterMetric oemcrypto_generate_nonce = 27; + repeated DistributionMetric oemcrypto_generate_rsa_signature_time_us = 28; + repeated DistributionMetric oemcrypto_generate_signature_time_us = 29; + repeated DistributionMetric oemcrypto_generic_decrypt_time_us = 30; + repeated DistributionMetric oemcrypto_generic_encrypt_time_us = 31; + repeated DistributionMetric oemcrypto_generic_sign_time_us = 32; + repeated DistributionMetric oemcrypto_generic_verify_time_us = 33; + repeated CounterMetric oemcrypto_get_device_id = 34; + repeated DistributionMetric oemcrypto_get_key_data_time_us = 35; + repeated CounterMetric oemcrypto_get_oem_public_certificate = 36; + repeated CounterMetric oemcrypto_get_random = 37; + repeated DistributionMetric oemcrypto_initialize_time_us = 38; + optional ValueMetric oemcrypto_is_anti_rollback_hw_present = 39; + optional ValueMetric oemcrypto_is_keybox_valid = 40; + repeated DistributionMetric oemcrypto_load_device_rsa_key_time_us = 41; + repeated DistributionMetric oemcrypto_load_entitled_keys_time_us = 42; + repeated DistributionMetric oemcrypto_load_keys_time_us = 43; + optional ValueMetric oemcrypto_max_hdcp_capability = 44; + optional ValueMetric oemcrypto_max_number_of_sessions = 45; + optional ValueMetric oemcrypto_number_of_open_sessions = 46; + optional ValueMetric oemcrypto_provisioning_method = 47; + repeated DistributionMetric oemcrypto_refresh_keys_time_us = 48; + repeated CounterMetric oemcrypto_report_usage = 49; + repeated DistributionMetric oemcrypto_rewrap_device_rsa_key_time_us = 50; + repeated DistributionMetric oemcrypto_rewrap_device_rsa_key_30_time_us = 51; + optional ValueMetric oemcrypto_security_patch_level = 52; + repeated DistributionMetric oemcrypto_select_key_time_us = 53; + optional ValueMetric oemcrypto_usage_table_support = 54; + repeated CounterMetric oemcrypto_update_usage_table = 55; + repeated CounterMetric oemcrypto_update_usage_entry = 57; + } + + // This contains metrics that were captured within a CdmSession. This contains + // nested CryptoMetrics that were captured in the context of the session. + // next id: 8 + message SessionMetrics { + optional ValueMetric session_id = 1; + optional CryptoMetrics crypto_metrics = 2; + optional ValueMetric cdm_session_life_span_ms = 3; + repeated DistributionMetric cdm_session_renew_key_time_us = 4; + repeated CounterMetric cdm_session_restore_offline_session = 5; + repeated CounterMetric cdm_session_restore_usage_session = 6; + repeated DistributionMetric cdm_session_license_request_latency_ms = 7; + } + + // These are metrics recorded at the Engine level. This includes CryptoSession + // metrics that were captured in the context of the engine. + // next id: 29 + message EngineMetrics { + optional CryptoMetrics crypto_metrics = 1; + + // OEMCrypto Initialize Metrics. + optional ValueMetric oemcrypto_initialization_mode = 3; + optional ValueMetric oemcrypto_l1_api_version = 4; + optional ValueMetric oemcrypto_l1_min_api_version = 5; + // CdmEngine Metrics. + optional ValueMetric app_package_name = 6; + repeated DistributionMetric cdm_engine_add_key_time_us = 7; + optional ValueMetric cdm_engine_cdm_version = 8; + repeated CounterMetric cdm_engine_close_session = 9; + optional ValueMetric cdm_engine_creation_time_millis = 10; + repeated DistributionMetric cdm_engine_decrypt_time_us = 11; + repeated CounterMetric cdm_engine_find_session_for_key = 12; + repeated DistributionMetric cdm_engine_generate_key_request_time_us = 13; + repeated DistributionMetric cdm_engine_get_provisioning_request_time_us = 14; + repeated CounterMetric cdm_engine_get_secure_stop_ids = 15; + repeated DistributionMetric cdm_engine_get_usage_info_time_us = 16; + repeated DistributionMetric cdm_engine_handle_provisioning_response_time_us = 17; + optional ValueMetric cdm_engine_life_span_ms = 18; + repeated CounterMetric cdm_engine_open_key_set_session = 19; + repeated CounterMetric cdm_engine_open_session = 20; + repeated DistributionMetric cdm_engine_query_key_status_time_us = 21; + repeated CounterMetric cdm_engine_release_all_usage_info = 22; + repeated CounterMetric cdm_engine_release_usage_info = 23; + repeated CounterMetric cdm_engine_remove_all_usage_info = 24; + repeated CounterMetric cdm_engine_remove_keys = 25; + repeated CounterMetric cdm_engine_remove_usage_info = 26; + repeated DistributionMetric cdm_engine_restore_key_time_us = 27; + repeated CounterMetric cdm_engine_unprovision = 28; + } + + optional EngineMetrics engine_metrics = 1; + repeated SessionMetrics session_metrics = 2; +} + +// This message contains a collection of metrics, one per CDM engine instance. +message WvCdmMetricsGroup { + repeated WvCdmMetrics metrics = 1; +} + +// Test message to support unit testing. +message TestMetrics{ + optional ValueMetric test_value_metric = 1; + repeated CounterMetric test_counters = 3; + repeated DistributionMetric test_distributions = 2; +} diff --git a/libwvdrmengine/tools/metrics_dump/src/base64decode.cpp b/libwvdrmengine/tools/metrics_dump/src/base64decode.cpp new file mode 100644 index 00000000..722a6c21 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/src/base64decode.cpp @@ -0,0 +1,89 @@ +// 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. +// +// simple base64 decoder + +#include +#include +#include + +#include "base64decode.h" + +namespace base64 { + +using namespace std; + +string Base64::Decode(const string &encoded) { + string s = encoded; + + while (s.size() % 4 != 0) { + s.append("="); + } + + size_t n = s.size(); + + size_t padding = 0; + if (n >= 1 && s[n - 1] == '=') { + padding = 1; + + if (n >= 2 && s[n - 2] == '=') { + padding = 2; + + if (n >= 3 && s[n - 3] == '=') { + padding = 3; + } + } + } + + // We divide first to avoid overflow. It's OK to do this because we + // already made sure that n % 4 == 0. + size_t outLen = (n / 4) * 3 - padding; + + std::unique_ptr buffer(new uint8_t[outLen]); + if (!buffer) { + return ""; + } + uint8_t *out = buffer.get(); + size_t j = 0; + uint32_t accum = 0; + for (size_t i = 0; i < n; ++i) { + char c = s[i]; + unsigned value; + if (c >= 'A' && c <= 'Z') { + value = c - 'A'; + } else if (c >= 'a' && c <= 'z') { + value = 26 + c - 'a'; + } else if (c >= '0' && c <= '9') { + value = 52 + c - '0'; + } else if (c == '+' || c == '-') { + value = 62; + } else if (c == '/' || c == '_') { + value = 63; + } else { + if (c != '=') { + std::cerr << "unexpected character in base64 decode:" << c << std::endl; + } + value = 0; + } + + accum = (accum << 6) | value; + + if (((i + 1) % 4) == 0) { + if (j < outLen) { + out[j++] = (accum >> 16); + } + if (j < outLen) { + out[j++] = (accum >> 8) & 0xff; + } + if (j < outLen) { + out[j++] = accum & 0xff; + } + + accum = 0; + } + } + return string((char *)buffer.get(), outLen); +} + +} // namespace base64 diff --git a/libwvdrmengine/tools/metrics_dump/src/mediadrm_metrics.cpp b/libwvdrmengine/tools/metrics_dump/src/mediadrm_metrics.cpp new file mode 100644 index 00000000..e074d82a --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/src/mediadrm_metrics.cpp @@ -0,0 +1,173 @@ +// 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 mediadrm framework protobuf metrics + +#include +#include +#include +#include +#include +#include + +#include "mediadrm_metrics.pb.h" + +namespace mediadrm_metrics { + +using namespace android::drm_metrics; +using std::string; +using std::to_string; + +const string kIndentPerLine = " "; + +string FormatAttributes(const DrmFrameworkMetrics::Attributes& attributes) { + string result; + if (attributes.has_error_code()) { + result.append("error_code:"); + result.append(to_string(attributes.error_code())); + } + if (attributes.has_key_status_type()) { + if (result.size()) result.append(","); + result.append("key_status_type:"); + result.append(to_string(attributes.key_status_type())); + } + if (attributes.has_event_type()) { + if (result.size()) result.append(","); + result.append("event_type:"); + result.append(to_string(attributes.event_type())); + } + if (result.size()) { + return string(" {") + result + "}"; + } else { + return ""; + } +} + +string FormatCounter(const DrmFrameworkMetrics::Counter& cm) { + string result; + if (cm.has_count()) { + result = string("count=") + to_string(cm.count()); + if (cm.has_attributes()) { + result.append(FormatAttributes(cm.attributes())); + } + } + return result; +} + +string FormatDistributionMetric( + const DrmFrameworkMetrics::DistributionMetric& dm) { + string result; + if (dm.has_operation_count()) { + if (dm.operation_count() == 1) { + if (dm.has_mean()) { + std::ostringstream buffer; + buffer << dm.mean(); + result += string("mean=") + buffer.str(); + } + } else { + if (dm.has_min()) { + std::ostringstream buffer; + buffer << dm.min(); + result = string("min=") + buffer.str(); + } + if (dm.has_max()) { + std::ostringstream buffer; + buffer << dm.max(); + if (result.size()) result.append(" "); + result += string("max=") + buffer.str(); + } + if (dm.has_mean()) { + std::ostringstream buffer; + buffer << dm.mean(); + if (result.size()) result.append(" "); + result += string("mean=") + buffer.str(); + } + if (dm.has_variance()) { + std::ostringstream buffer; + buffer << dm.variance(); + if (result.size()) result.append(" "); + result += string("variance=") + buffer.str(); + } + if (dm.has_operation_count()) { + if (result.size()) result.append(" "); + result += string("count=") + to_string(dm.operation_count()); + } + } + if (dm.has_attributes()) { + result.append(FormatAttributes(dm.attributes())); + } + } + return result; +} + +#define FORMAT_REPEATED_DISTRIBUTION(NAME, INDENT) \ + if (metrics.NAME##_size() == 1) { \ + result.append(INDENT + #NAME + ": "); \ + result.append(FormatDistributionMetric(metrics.NAME(0)) + "\n"); \ + } else { \ + for (int i = 0; i < metrics.NAME##_size(); i++) { \ + result.append(INDENT + #NAME "[" + to_string(i) + "]: "); \ + result.append(FormatDistributionMetric(metrics.NAME(i)) + "\n"); \ + } \ + } + +#define FORMAT_REPEATED_COUNTER(NAME, INDENT) \ + if (metrics.NAME##_size() == 1) { \ + result.append(INDENT + #NAME ": "); \ + result.append(FormatCounter(metrics.NAME(0)) + "\n"); \ + } else { \ + for (int i = 0; i < metrics.NAME##_size(); i++) { \ + result.append(INDENT + #NAME "[" + to_string(i) + "]: "); \ + result.append(FormatCounter(metrics.NAME(i)) + "\n"); \ + } \ + } + +uint8_t hex2bin(char c) { return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10; } + +string hexdecode(string encoded) { + string result; + for (size_t i = 0; i < encoded.size(); i += 2) { + char c = hex2bin(encoded[i]) << 4 | hex2bin(encoded[i + 1]); + result += c; + } + return result; +} + +string ms_to_seconds(uint64_t time_ms) { + uint64_t time_s = time_ms / 1000; + uint64_t ms = time_ms - time_s * 1000; + return to_string(time_s) + "." + to_string(ms); +} + +void FormatDrmFrameworkMetrics(const DrmFrameworkMetrics& metrics, + string& result) { + string indent = kIndentPerLine; + + result.append("mediadrm_metrics\n"); + FORMAT_REPEATED_COUNTER(open_session_counter, indent); + FORMAT_REPEATED_COUNTER(close_session_counter, indent); + FORMAT_REPEATED_DISTRIBUTION(get_key_request_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(provide_key_response_time_us, indent); + FORMAT_REPEATED_COUNTER(get_provisioning_request_counter, indent); + FORMAT_REPEATED_COUNTER(provide_provisioning_response_counter, indent); + FORMAT_REPEATED_COUNTER(key_status_change_counter, indent); + FORMAT_REPEATED_COUNTER(event_callback_counter, indent); + FORMAT_REPEATED_COUNTER(get_device_unique_id_counter, indent); + + size_t i = 0; + for (auto const& entry : metrics.session_lifetimes()) { + DrmFrameworkMetrics_SessionLifetime lifetime = entry.second; + if (lifetime.has_start_time_ms() && lifetime.has_end_time_ms()) { + result += indent + "session[" + to_string(++i) + "]: \"" + + hexdecode(entry.first) + "\""; + result += + string(" start: ") + ms_to_seconds(lifetime.start_time_ms()) + " sec"; + result += string(" duration: ") + + to_string(lifetime.end_time_ms() - lifetime.start_time_ms()) + + " ms\n"; + } + } +} + +}; // namespace mediadrm_metrics diff --git a/libwvdrmengine/tools/metrics_dump/src/metrics_dump.cpp b/libwvdrmengine/tools/metrics_dump/src/metrics_dump.cpp new file mode 100644 index 00000000..73074714 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/src/metrics_dump.cpp @@ -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 +#include +#include +#include +#include +#include + +#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 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] []" << 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 |" << endl; + cerr << " ignore metrics from the specified package or component" + << endl; + cerr << " --select |" << 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; +} diff --git a/libwvdrmengine/tools/metrics_dump/src/wv_metrics.cpp b/libwvdrmengine/tools/metrics_dump/src/wv_metrics.cpp new file mode 100644 index 00000000..208bdc64 --- /dev/null +++ b/libwvdrmengine/tools/metrics_dump/src/wv_metrics.cpp @@ -0,0 +1,415 @@ +// 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 widevine protobuf metrics + +#include +#include +#include +#include +#include + +#include "metrics.pb.h" +#include "OEMCryptoCENC.h" +#include "test_printers.h" +#include "wv_cdm_types.h" + +namespace wv_metrics { + +using namespace drm_metrics; +using std::string; +using std::to_string; + +const string kIndentPerLine = " "; + +string FormatCdmErrorTranslation(int error_code) { + std::stringstream os; + os << static_cast(error_code); + return " " + os.str(); +} + +string FormatOEMCryptoResult(int oemcrypto_result) { + std::stringstream os; + os << static_cast(oemcrypto_result); + return " " + os.str(); +} + +string FormatOEMCryptoInitializeMode(const ValueMetric& vm) { + std::map translations = { + {0, "USING_IN_APP"}, + {1, "FORCING_L3"}, + {2, "USING_L3_NO_L1_LIBRARY_PATH"}, + {3, "USING_L3_L1_OPEN_FAILED"}, + {4, "USING_L3_L1_LOAD_FAILED"}, + {5, "USING_L3_COULD_NOT_INITIALIZE_L1"}, + {6, "USING_L3_WRONG_L1_VERSION"}, + {7, "USING_L1_WITH_KEYBOX"}, + {8, "USING_L1_WITH_CERTIFICATE"}, + {9, "USING_L1_CERTIFICATE_MIX"}, + {10, "USING_L3_BAD_KEYBOX"}, + {11, "USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX"}, + {12, "USING_L3_COULD_NOT_INSTALL_KEYBOX"}, + {13, "USING_L1_INSTALLED_KEYBOX"}, + {14, "USING_L3_INVALID_L1"}, + {15, "USING_L1_WITH_PROVISIONING_3_0"}, + {16, "L3_INITIALIZATION_FAILED"}}; + return translations[vm.int_value()]; +} + +string FormatOEMCryptoHdcpCapability(const ValueMetric& vm) { + std::map translations = {{0, "HDCP_NONE"}, {1, "HDCP_V1"}, + {2, "HDCP_V2"}, {3, "HDCP_V2_1"}, + {4, "HDCP_V2_2"}, {0xff, "NO_OUTPUT"}}; + return translations[vm.int_value()]; +} + +string FormatOEMCryptoProvisioningMethod(const ValueMetric& vm) { + std::map translations = {{0, "PROVISIONING_ERROR"}, + {1, "DRM_CERTIFICATE"}, + {2, "KEYBOX"}, + {3, "OEM_CERTIFICATE"}}; + return translations[vm.int_value()]; +} + +string FormatAttributes(const Attributes& attributes) { + string result; + if (attributes.has_error_code()) { + result.append("error_code:"); + result.append(to_string(attributes.error_code())); + result.append(FormatCdmErrorTranslation(attributes.error_code())); + } + if (attributes.has_error_code_bool()) { + if (result.size()) result.append(","); + result.append("success:"); + result.append(attributes.error_code_bool() ? "true" : "false"); + } + if (attributes.has_cdm_security_level()) { + if (result.size()) result.append(","); + result.append("cdm_security_level:"); + result.append(to_string(attributes.cdm_security_level())); + } + if (attributes.has_security_level()) { + if (result.size()) result.append(","); + result.append("security_level:"); + result.append(to_string(attributes.security_level())); + } + if (attributes.has_length()) { + if (result.size()) result.append(","); + result.append("length:"); + result.append(to_string(attributes.length())); + } + if (attributes.has_encryption_algorithm()) { + if (result.size()) result.append(","); + result.append("encryption_algorithm:"); + result.append(to_string(attributes.encryption_algorithm())); + } + if (attributes.has_signing_algorithm()) { + if (result.size()) result.append(","); + result.append("signing_algorithm:"); + result.append(to_string(attributes.signing_algorithm())); + } + if (attributes.has_oem_crypto_result()) { + if (result.size()) result.append(","); + result.append("oemcrypto_result:"); + result.append(to_string(attributes.oem_crypto_result())); + result.append(FormatOEMCryptoResult(attributes.oem_crypto_result())); + } + if (attributes.has_key_status_type()) { + if (result.size()) result.append(","); + result.append("key_status_type:"); + result.append(to_string(attributes.key_status_type())); + } + if (attributes.has_event_type()) { + if (result.size()) result.append(","); + result.append("event_type:"); + result.append(to_string(attributes.event_type())); + } + if (attributes.has_key_request_type()) { + if (result.size()) result.append(","); + result.append("key_request_type:"); + result.append(to_string(attributes.key_request_type())); + } + if (result.size()) { + return string(" {") + result + "}"; + } else { + return ""; + } +} + +string FormatCounterMetric(const CounterMetric& cm) { + string result; + if (cm.has_count()) { + result = string("count=") + to_string(cm.count()); + if (cm.has_attributes()) { + result.append(FormatAttributes(cm.attributes())); + } + } + return result; +} + +string FormatDistributionMetric(const DistributionMetric& dm) { + string result; + if (dm.has_min()) { + std::ostringstream buffer; + buffer << dm.min(); + result = string("min=") + buffer.str(); + } + if (dm.has_max()) { + std::ostringstream buffer; + buffer << dm.max(); + if (result.size()) result.append(" "); + result += string("max=") + buffer.str(); + } + if (dm.has_mean()) { + std::ostringstream buffer; + buffer << dm.mean(); + if (result.size()) result.append(" "); + result += string("mean=") + buffer.str(); + } + if (dm.has_variance()) { + std::ostringstream buffer; + buffer << dm.variance(); + if (result.size()) result.append(" "); + result += string("variance=") + buffer.str(); + } + if (dm.has_operation_count()) { + if (result.size()) result.append(" "); + result += string("count=") + to_string(dm.operation_count()); + } + if (dm.has_attributes()) { + result.append(FormatAttributes(dm.attributes())); + } + return result; +} + +string FormatValueMetric(const ValueMetric& vm) { + string result; + if (vm.has_error_code()) { + result.append("error(" + to_string(vm.error_code())); + result.append(FormatCdmErrorTranslation(vm.error_code())); + result.append(")"); + } + if (vm.has_int_value()) { + result.append(to_string(vm.int_value())); + } + if (vm.has_double_value()) { + std::ostringstream buffer; + buffer << vm.double_value(); + result.append(buffer.str()); + } + if (vm.has_string_value()) { + result.append("\""); + result.append(vm.string_value()); + result.append("\""); + } + return result; +} + +#define FORMAT_REPEATED_DISTRIBUTION(NAME, INDENT) \ + if (metrics.NAME##_size() == 1) { \ + result.append(INDENT + #NAME + ": "); \ + result.append(FormatDistributionMetric(metrics.NAME(0)) + "\n"); \ + } else { \ + for (int i = 0; i < metrics.NAME##_size(); i++) { \ + result.append(INDENT + #NAME "[" + to_string(i) + "]: "); \ + result.append(FormatDistributionMetric(metrics.NAME(i)) + "\n"); \ + } \ + } + +#define FORMAT_REPEATED_COUNTER(NAME, INDENT) \ + if (metrics.NAME##_size() == 1) { \ + result.append(INDENT + #NAME ": "); \ + result.append(FormatCounterMetric(metrics.NAME(0)) + "\n"); \ + } else { \ + for (int i = 0; i < metrics.NAME##_size(); i++) { \ + result.append(INDENT + #NAME "[" + to_string(i) + "]: "); \ + result.append(FormatCounterMetric(metrics.NAME(i)) + "\n"); \ + } \ + } + +#define FORMAT_OPTIONAL_VALUE(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + result.append(INDENT + #NAME ": " + FormatValueMetric(metrics.NAME()) + \ + "\n"); \ + } + +#define FORMAT_OPTIONAL_INITIALIZATION_MODE(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + result.append(INDENT + #NAME ": "); \ + result.append(FormatOEMCryptoInitializeMode(metrics.NAME()) + "\n"); \ + } + +#define FORMAT_OPTIONAL_HDCP_CAPABILITY(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + result.append(INDENT + #NAME ": "); \ + result.append(FormatOEMCryptoHdcpCapability(metrics.NAME()) + "\n"); \ + } + +#define FORMAT_OPTIONAL_PROVISIONING_METHOD(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + result.append(INDENT + #NAME ": "); \ + result.append(FormatOEMCryptoProvisioningMethod(metrics.NAME()) + "\n"); \ + } + +#define FORMAT_OPTIONAL_CRYPTO_METRICS(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + FormatCryptoMetrics(metrics.NAME(), INDENT + kIndentPerLine, result); \ + } + +void FormatCryptoMetrics(const WvCdmMetrics_CryptoMetrics metrics, + const string& indent, string& result) { + // Crypto Session Metrics. + FORMAT_OPTIONAL_VALUE(crypto_session_security_level, indent); + FORMAT_REPEATED_COUNTER(crypto_session_delete_all_usage_reports, indent); + FORMAT_REPEATED_COUNTER(crypto_session_delete_multiple_usage_information, + indent); + FORMAT_REPEATED_DISTRIBUTION(crypto_session_generic_decrypt_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(crypto_session_generic_encrypt_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(crypto_session_generic_sign_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(crypto_session_generic_verify_time_us, indent); + FORMAT_REPEATED_COUNTER(crypto_session_get_device_unique_id, indent); + FORMAT_REPEATED_COUNTER(crypto_session_get_token, indent); + FORMAT_OPTIONAL_VALUE(crypto_session_life_span, indent); + FORMAT_REPEATED_DISTRIBUTION( + crypto_session_load_certificate_private_key_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(crypto_session_open_time_us, indent); + FORMAT_OPTIONAL_VALUE(crypto_session_system_id, indent); + FORMAT_REPEATED_DISTRIBUTION(crypto_session_update_usage_information_time_us, + indent); + FORMAT_OPTIONAL_VALUE(crypto_session_usage_information_support, indent); + + // Usage Table Metrics + FORMAT_OPTIONAL_VALUE(usage_table_header_initial_size, indent); + FORMAT_REPEATED_COUNTER(usage_table_header_add_entry, indent); + FORMAT_REPEATED_COUNTER(usage_table_header_delete_entry, indent); + FORMAT_REPEATED_DISTRIBUTION(usage_table_header_update_entry_time_us, indent); + FORMAT_REPEATED_COUNTER(usage_table_header_load_entry, indent); + + // OemCrypto metrics. + FORMAT_OPTIONAL_VALUE(oemcrypto_api_version, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_close_session, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_copy_buffer_time_us, indent); + FORMAT_OPTIONAL_HDCP_CAPABILITY(oemcrypto_current_hdcp_capability, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_deactivate_usage_entry, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_decrypt_cenc_time_us, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_delete_usage_entry, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_delete_usage_table, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_derive_keys_from_session_key_time_us, + indent); + FORMAT_REPEATED_COUNTER(oemcrypto_force_delete_usage_entry, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_generate_derived_keys_time_us, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_generate_nonce, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_generate_rsa_signature_time_us, + indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_generate_signature_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_generic_decrypt_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_generic_encrypt_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_generic_sign_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_generic_verify_time_us, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_get_device_id, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_get_key_data_time_us, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_get_oem_public_certificate, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_get_random, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_initialize_time_us, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_is_anti_rollback_hw_present, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_is_keybox_valid, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_load_device_rsa_key_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_load_entitled_keys_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_load_keys_time_us, indent); + FORMAT_OPTIONAL_HDCP_CAPABILITY(oemcrypto_max_hdcp_capability, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_max_number_of_sessions, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_number_of_open_sessions, indent); + FORMAT_OPTIONAL_PROVISIONING_METHOD(oemcrypto_provisioning_method, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_refresh_keys_time_us, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_report_usage, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_rewrap_device_rsa_key_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_rewrap_device_rsa_key_30_time_us, + indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_security_patch_level, indent); + FORMAT_REPEATED_DISTRIBUTION(oemcrypto_select_key_time_us, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_usage_table_support, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_update_usage_table, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_update_usage_entry, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_create_usage_table_header, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_load_usage_table_header, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_shrink_usage_table_header, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_create_new_usage_entry, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_load_usage_entry, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_move_entry, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_create_old_usage_entry, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_copy_old_usage_entry, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_set_sandbox, indent); + FORMAT_REPEATED_COUNTER(oemcrypto_set_decrypt_hash, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_resource_rating_tier, indent); +} + +void FormatSessionMetrics(const WvCdmMetrics_SessionMetrics& metrics, + const string& indent, string& result) { + FORMAT_OPTIONAL_VALUE(session_id, indent); + FORMAT_OPTIONAL_CRYPTO_METRICS(crypto_metrics, indent); + FORMAT_OPTIONAL_VALUE(cdm_session_life_span_ms, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_session_renew_key_time_us, indent); + FORMAT_REPEATED_COUNTER(cdm_session_restore_offline_session, indent); + FORMAT_REPEATED_COUNTER(cdm_session_restore_usage_session, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_session_license_request_latency_ms, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_build_info, indent); + FORMAT_OPTIONAL_VALUE(license_sdk_version, indent); + FORMAT_OPTIONAL_VALUE(license_service_version, indent); +} + +void FormatEngineMetrics(const WvCdmMetrics_EngineMetrics& metrics, + const string& indent, string& result) { + FORMAT_OPTIONAL_CRYPTO_METRICS(crypto_metrics, indent); + + // OEMCrypto Initialize Metrics. + FORMAT_OPTIONAL_INITIALIZATION_MODE(oemcrypto_initialization_mode, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_l1_api_version, indent); + FORMAT_OPTIONAL_VALUE(oemcrypto_l1_min_api_version, indent); + FORMAT_OPTIONAL_VALUE(level3_oemcrypto_initialization_error, indent); + FORMAT_OPTIONAL_VALUE(previous_oemcrypto_initialization_failure, indent); + + // CdmEngine Metrics. + FORMAT_OPTIONAL_VALUE(app_package_name, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_add_key_time_us, indent); + FORMAT_OPTIONAL_VALUE(cdm_engine_cdm_version, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_close_session, indent); + FORMAT_OPTIONAL_VALUE(cdm_engine_creation_time_millis, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_decrypt_time_us, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_find_session_for_key, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_generate_key_request_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_get_provisioning_request_time_us, + indent); + FORMAT_REPEATED_COUNTER(cdm_engine_get_secure_stop_ids, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_get_usage_info_time_us, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_handle_provisioning_response_time_us, + indent); + FORMAT_OPTIONAL_VALUE(cdm_engine_life_span_ms, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_open_key_set_session, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_open_session, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_query_key_status_time_us, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_release_all_usage_info, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_release_usage_info, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_remove_all_usage_info, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_remove_keys, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_remove_usage_info, indent); + FORMAT_REPEATED_DISTRIBUTION(cdm_engine_restore_key_time_us, indent); + FORMAT_REPEATED_COUNTER(cdm_engine_unprovision, indent); +} + +void FormatWvCdmMetrics(const WvCdmMetrics& metrics, string& result) { + string indent = kIndentPerLine; + string next_indent = indent + kIndentPerLine; + + result.append("engine_metrics\n"); + if (metrics.has_engine_metrics()) { + FormatEngineMetrics(metrics.engine_metrics(), indent, result); + } + result.append("session_metrics\n"); + for (int i = 0; i < metrics.session_metrics_size(); i++) { + result.append(indent + "session[" + to_string(i) + "]\n"); + FormatSessionMetrics(metrics.session_metrics(i), next_indent, result); + } +} + +}; // namespace wv_metrics