Merge "Expose libstatspull as a stable C API"

This commit is contained in:
Howard Ro 2020-02-05 18:28:12 +00:00 committed by Android (Google) Code Review
commit f49c38beb5
12 changed files with 482 additions and 407 deletions

View File

@ -32,7 +32,7 @@ cc_library_shared {
],
export_include_dirs: ["include"],
shared_libs: [
//TODO: use libbinder_ndk.
//TODO: use libbinder_ndk. Remove libservices.
"libbinder",
"libstatssocket",
"libservices",
@ -40,5 +40,12 @@ cc_library_shared {
static_libs: [
"liblog",
"libutils",
]
],
// enumerate stable entry points for APEX use
stubs: {
symbol_file: "libstatspull.map.txt",
versions: [
"30",
],
},
}

View File

@ -15,41 +15,122 @@
*/
#pragma once
#include <stats_event.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Metadata for registering a stats_pull_atom_callback.
/**
* Opaque struct representing the metadata for registering an AStatsManager_PullAtomCallback.
*/
typedef struct pull_atom_metadata {
int64_t cool_down_ns;
int64_t timeout_ns;
int32_t* additive_fields;
int32_t additive_fields_size;
} pull_atom_metadata;
struct AStatsManager_PullAtomMetadata;
typedef struct AStatsManager_PullAtomMetadata AStatsManager_PullAtomMetadata;
typedef struct pulled_stats_event_list pulled_stats_event_list;
/**
* Allocate and initialize new PullAtomMetadata.
*
* Must call AStatsManager_PullAtomMetadata_release to free the memory.
*/
AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain();
typedef int32_t status_pull_atom_return_t;
/**
* Frees the memory held by this PullAtomMetadata
*
* After calling this, the PullAtomMetadata must not be used or modified in any way.
*/
void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata);
/**
* Set the cool down time of the pull in nanoseconds. If two successive pulls are issued
* within the cool down, a cached version of the first will be used for the second.
*/
void AStatsManager_PullAtomMetadata_setCoolDownNs(AStatsManager_PullAtomMetadata* metadata,
int64_t cool_down_ns);
/**
* Set the maximum time the pull can take in nanoseconds.
*/
void AStatsManager_PullAtomMetadata_setTimeoutNs(AStatsManager_PullAtomMetadata* metadata,
int64_t timeout_ns);
/**
* Set the additive fields of this pulled atom.
*
* This is only applicable for atoms which have a uid field. When tasks are run in
* isolated processes, the data will be attributed to the host uid. Additive fields
* will be combined when the non-additive fields are the same.
*/
void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
int* additive_fields, int num_fields);
/**
* Return codes for the result of a pull.
*/
typedef int32_t AStatsManager_PullAtomCallbackReturn;
enum {
STATS_PULL_SUCCESS = 0,
STATS_PULL_SKIP = 1,
// Value indicating that this pull was successful and that the result should be used.
AStatsManager_PULL_SUCCESS = 0,
// Value indicating that this pull was unsuccessful and that the result should not be used.
AStatsManager_PULL_SKIP = 1,
};
typedef status_pull_atom_return_t (*stats_pull_atom_callback_t)(int32_t atom_tag,
pulled_stats_event_list* data,
void* cookie);
/**
* Opaque struct representing a list of AStatsEvent objects.
*/
struct AStatsEventList;
typedef struct AStatsEventList AStatsEventList;
struct stats_event* add_stats_event_to_pull_data(pulled_stats_event_list* pull_data);
/**
* Appends and returns an AStatsEvent to the end of the AStatsEventList.
*
* If an AStatsEvent is obtained in this manner, the memory is internally managed and
* AStatsEvent_release does not need to be called. The lifetime of the AStatsEvent is that of the
* AStatsEventList.
*
* The AStatsEvent does still need to be built by calling AStatsEvent_build.
*/
AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data);
void register_stats_pull_atom_callback(int32_t atom_tag, stats_pull_atom_callback_t callback,
pull_atom_metadata* metadata, void* cookie);
/**
* Callback interface for pulling atoms requested by the stats service.
*
* \param atom_tag the tag of the atom to pull.
* \param data an output parameter in which the caller should fill the results of the pull. This
* param cannot be NULL and it's lifetime is as long as the execution of the callback.
* It must not be accessed or modified after returning from the callback.
* \param cookie the opaque pointer passed in AStatsManager_registerPullAtomCallback.
* \return AStatsManager_PULL_SUCCESS if the pull was successful, or AStatsManager_PULL_SKIP if not.
*/
typedef AStatsManager_PullAtomCallbackReturn (*AStatsManager_PullAtomCallback)(
int32_t atom_tag, AStatsEventList* data, void* cookie);
/**
* Registers a callback for an atom when that atom is to be pulled. The stats service will
* invoke the callback when the stats service determines that this atom needs to be
* pulled.
*
* \param atom_tag The tag of the atom for this pull atom callback.
* \param metadata Optional metadata specifying the timeout, cool down time, and
* additive fields for mapping isolated to host uids.
* This param is nullable, in which case defaults will be used.
* \param callback The callback to be invoked when the stats service pulls the atom.
* \param cookie A pointer that will be passed back to the callback.
* It has no meaning to statsd.
*/
void AStatsManager_registerPullAtomCallback(int32_t atom_tag,
AStatsManager_PullAtomCallback callback,
AStatsManager_PullAtomMetadata* metadata, void* cookie);
void unregister_stats_pull_atom_callback(int32_t atom_tag);
/**
* Unregisters a callback for an atom when that atom is to be pulled. Note that any ongoing
* pulls will still occur.
*
* \param atomTag The tag of the atom of which to unregister
*/
void AStatsManager_unregisterPullAtomCallback(int32_t atom_tag);
#ifdef __cplusplus
}

View File

@ -0,0 +1,13 @@
LIBSTATSPULL {
global:
AStatsManager_PullAtomMetadata_obtain; # apex # introduced=30
AStatsManager_PullAtomMetadata_release; # apex # introduced=30
AStatsManager_PullAtomMetadata_setCoolDownNs; # apex # introduced=30
AStatsManager_PullAtomMetadata_setTimeoutNs; # apex # introduced=30
AStatsManager_PullAtomMetadata_setAdditiveFields; # apex # introduced=30
AStatsEventList_addStatsEvent; # apex # introduced=30
AStatsManager_registerPullAtomCallback; # apex # introduced=30
AStatsManager_unregisterPullAtomCallback; # apex # introduced=30
local:
*;
};

View File

@ -28,12 +28,12 @@
#include <thread>
struct pulled_stats_event_list {
std::vector<stats_event*> data;
struct AStatsEventList {
std::vector<AStatsEvent*> data;
};
struct stats_event* add_stats_event_to_pull_data(pulled_stats_event_list* pull_data) {
struct stats_event* event = stats_event_obtain();
AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {
AStatsEvent* event = AStatsEvent_obtain();
pull_data->data.push_back(event);
return event;
}
@ -41,9 +41,42 @@ struct stats_event* add_stats_event_to_pull_data(pulled_stats_event_list* pull_d
static const int64_t DEFAULT_COOL_DOWN_NS = 1000000000LL; // 1 second.
static const int64_t DEFAULT_TIMEOUT_NS = 10000000000LL; // 10 seconds.
struct AStatsManager_PullAtomMetadata {
int64_t cool_down_ns;
int64_t timeout_ns;
std::vector<int32_t> additive_fields;
};
AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {
AStatsManager_PullAtomMetadata* metadata = new AStatsManager_PullAtomMetadata();
metadata->cool_down_ns = DEFAULT_COOL_DOWN_NS;
metadata->timeout_ns = DEFAULT_TIMEOUT_NS;
metadata->additive_fields = std::vector<int32_t>();
return metadata;
}
void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {
delete metadata;
}
void AStatsManager_PullAtomMetadata_setCoolDownNs(AStatsManager_PullAtomMetadata* metadata,
int64_t cool_down_ns) {
metadata->cool_down_ns = cool_down_ns;
}
void AStatsManager_PullAtomMetadata_setTimeoutNs(AStatsManager_PullAtomMetadata* metadata,
int64_t timeout_ns) {
metadata->timeout_ns = timeout_ns;
}
void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
int* additive_fields, int num_fields) {
metadata->additive_fields.assign(additive_fields, additive_fields + num_fields);
}
class StatsPullAtomCallbackInternal : public android::os::BnPullAtomCallback {
public:
StatsPullAtomCallbackInternal(const stats_pull_atom_callback_t callback, void* cookie,
StatsPullAtomCallbackInternal(const AStatsManager_PullAtomCallback callback, void* cookie,
const int64_t coolDownNs, const int64_t timeoutNs,
const std::vector<int32_t> additiveFields)
: mCallback(callback),
@ -55,15 +88,16 @@ class StatsPullAtomCallbackInternal : public android::os::BnPullAtomCallback {
::android::binder::Status onPullAtom(
int32_t atomTag,
const ::android::sp<::android::os::IPullAtomResultReceiver>& resultReceiver) override {
pulled_stats_event_list statsEventList;
AStatsEventList statsEventList;
statsEventList.data.clear();
int successInt = mCallback(atomTag, &statsEventList, mCookie);
bool success = successInt == STATS_PULL_SUCCESS;
bool success = successInt == AStatsManager_PULL_SUCCESS;
// Convert stats_events into StatsEventParcels.
std::vector<android::util::StatsEventParcel> parcels;
for (int i = 0; i < statsEventList.data.size(); i++) {
size_t size;
uint8_t* buffer = stats_event_get_buffer(statsEventList.data[i], &size);
uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
android::util::StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
@ -74,7 +108,7 @@ class StatsPullAtomCallbackInternal : public android::os::BnPullAtomCallback {
resultReceiver->pullFinished(atomTag, success, parcels);
for (int i = 0; i < statsEventList.data.size(); i++) {
stats_event_release(statsEventList.data[i]);
AStatsEvent_release(statsEventList.data[i]);
}
return android::binder::Status::ok();
}
@ -84,7 +118,7 @@ class StatsPullAtomCallbackInternal : public android::os::BnPullAtomCallback {
const std::vector<int32_t>& getAdditiveFields() const { return mAdditiveFields; }
private:
const stats_pull_atom_callback_t mCallback;
const AStatsManager_PullAtomCallback mCallback;
void* mCookie;
const int64_t mCoolDownNs;
const int64_t mTimeoutNs;
@ -165,15 +199,16 @@ void unregisterStatsPullAtomCallbackBlocking(int32_t atomTag) {
statsService->unregisterNativePullAtomCallback(atomTag);
}
void register_stats_pull_atom_callback(int32_t atom_tag, stats_pull_atom_callback_t callback,
pull_atom_metadata* metadata, void* cookie) {
void AStatsManager_registerPullAtomCallback(int32_t atom_tag,
AStatsManager_PullAtomCallback callback,
AStatsManager_PullAtomMetadata* metadata,
void* cookie) {
int64_t coolDownNs = metadata == nullptr ? DEFAULT_COOL_DOWN_NS : metadata->cool_down_ns;
int64_t timeoutNs = metadata == nullptr ? DEFAULT_TIMEOUT_NS : metadata->timeout_ns;
std::vector<int32_t> additiveFields;
if (metadata != nullptr && metadata->additive_fields != nullptr) {
additiveFields.assign(metadata->additive_fields,
metadata->additive_fields + metadata->additive_fields_size);
if (metadata != nullptr) {
additiveFields = metadata->additive_fields;
}
android::sp<StatsPullAtomCallbackInternal> callbackBinder = new StatsPullAtomCallbackInternal(
@ -189,7 +224,7 @@ void register_stats_pull_atom_callback(int32_t atom_tag, stats_pull_atom_callbac
registerThread.detach();
}
void unregister_stats_pull_atom_callback(int32_t atom_tag) {
void AStatsManager_unregisterPullAtomCallback(int32_t atom_tag) {
{
std::lock_guard<std::mutex> lg(pullAtomMutex);
// Always remove the puller from our map.

View File

@ -38,7 +38,7 @@ const bool StatsEventCompat::mPlatformAtLeastR =
// definitions of static class variables
bool StatsEventCompat::mAttemptedLoad = false;
struct stats_event_api_table* StatsEventCompat::mStatsEventApi = nullptr;
void* StatsEventCompat::mStatsEventApi = nullptr;
std::mutex StatsEventCompat::mLoadLock;
StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
@ -49,7 +49,8 @@ StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
if (!mAttemptedLoad) {
void* handle = dlopen("libstatssocket.so", RTLD_NOW);
if (handle) {
mStatsEventApi = (struct stats_event_api_table*)dlsym(handle, "table");
// mStatsEventApi = (struct AStatsEvent_apiTable*)dlsym(handle,
// "table");
} else {
ALOGE("dlopen failed: %s\n", dlerror());
}
@ -58,19 +59,19 @@ StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
}
if (mStatsEventApi) {
mEventR = mStatsEventApi->obtain();
// mEventR = mStatsEventApi->obtain();
} else if (!mPlatformAtLeastR) {
mEventQ << android::elapsedRealtimeNano();
}
}
StatsEventCompat::~StatsEventCompat() {
if (mStatsEventApi) mStatsEventApi->release(mEventR);
// if (mStatsEventApi) mStatsEventApi->release(mEventR);
}
void StatsEventCompat::setAtomId(int32_t atomId) {
if (mStatsEventApi) {
mStatsEventApi->set_atom_id(mEventR, (uint32_t)atomId);
// mStatsEventApi->setAtomId(mEventR, (uint32_t)atomId);
} else if (!mPlatformAtLeastR) {
mEventQ << atomId;
}
@ -78,7 +79,7 @@ void StatsEventCompat::setAtomId(int32_t atomId) {
void StatsEventCompat::writeInt32(int32_t value) {
if (mStatsEventApi) {
mStatsEventApi->write_int32(mEventR, value);
// mStatsEventApi->writeInt32(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
@ -86,7 +87,7 @@ void StatsEventCompat::writeInt32(int32_t value) {
void StatsEventCompat::writeInt64(int64_t value) {
if (mStatsEventApi) {
mStatsEventApi->write_int64(mEventR, value);
// mStatsEventApi->writeInt64(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
@ -94,7 +95,7 @@ void StatsEventCompat::writeInt64(int64_t value) {
void StatsEventCompat::writeFloat(float value) {
if (mStatsEventApi) {
mStatsEventApi->write_float(mEventR, value);
// mStatsEventApi->writeFloat(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
@ -102,7 +103,7 @@ void StatsEventCompat::writeFloat(float value) {
void StatsEventCompat::writeBool(bool value) {
if (mStatsEventApi) {
mStatsEventApi->write_bool(mEventR, value);
// mStatsEventApi->writeBool(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
@ -110,7 +111,7 @@ void StatsEventCompat::writeBool(bool value) {
void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
if (mStatsEventApi) {
mStatsEventApi->write_byte_array(mEventR, (const uint8_t*)buffer, length);
// mStatsEventApi->writeByteArray(mEventR, (const uint8_t*)buffer, length);
} else if (!mPlatformAtLeastR) {
mEventQ.AppendCharArray(buffer, length);
}
@ -120,7 +121,7 @@ void StatsEventCompat::writeString(const char* value) {
if (value == nullptr) value = "";
if (mStatsEventApi) {
mStatsEventApi->write_string8(mEventR, value);
// mStatsEventApi->writeString(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
@ -129,8 +130,8 @@ void StatsEventCompat::writeString(const char* value) {
void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
const vector<const char*>& tags) {
if (mStatsEventApi) {
mStatsEventApi->write_attribution_chain(mEventR, (const uint32_t*)uids, tags.data(),
(uint8_t)numUids);
// mStatsEventApi->writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),
// (uint8_t)numUids);
} else if (!mPlatformAtLeastR) {
mEventQ.begin();
for (size_t i = 0; i < numUids; i++) {
@ -148,26 +149,8 @@ void StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map,
const map<int, int64_t>& int64Map,
const map<int, const char*>& stringMap,
const map<int, float>& floatMap) {
if (mStatsEventApi) {
vector<struct key_value_pair> pairs;
for (const auto& it : int32Map) {
pairs.push_back({.key = it.first, .valueType = INT32_TYPE, .int32Value = it.second});
}
for (const auto& it : int64Map) {
pairs.push_back({.key = it.first, .valueType = INT64_TYPE, .int64Value = it.second});
}
for (const auto& it : stringMap) {
pairs.push_back({.key = it.first, .valueType = STRING_TYPE, .stringValue = it.second});
}
for (const auto& it : floatMap) {
pairs.push_back({.key = it.first, .valueType = FLOAT_TYPE, .floatValue = it.second});
}
mStatsEventApi->write_key_value_pairs(mEventR, pairs.data(), (uint8_t)pairs.size());
}
else if (!mPlatformAtLeastR) {
// Key value pairs are not supported with AStatsEvent.
if (!mPlatformAtLeastR) {
mEventQ.begin();
writeKeyValuePairMap(int32Map);
writeKeyValuePairMap(int64Map);
@ -194,19 +177,25 @@ template void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float
template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);
void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
if (mStatsEventApi) mStatsEventApi->add_bool_annotation(mEventR, annotationId, value);
// Workaround for unused params.
(void)annotationId;
(void)value;
// if (mStatsEventApi) mStatsEventApi->addBoolAnnotation(mEventR, annotationId, value);
// Don't do anything if on Q.
}
void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
if (mStatsEventApi) mStatsEventApi->add_int32_annotation(mEventR, annotationId, value);
// Workaround for unused params.
(void)annotationId;
(void)value;
// if (mStatsEventApi) mStatsEventApi->addInt32Annotation(mEventR, annotationId, value);
// Don't do anything if on Q.
}
int StatsEventCompat::writeToSocket() {
if (mStatsEventApi) {
mStatsEventApi->build(mEventR);
return mStatsEventApi->write(mEventR);
// mStatsEventApi->build(mEventR);
// return mStatsEventApi->write(mEventR);
}
if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS);

View File

@ -57,10 +57,11 @@ class StatsEventCompat {
const static bool mPlatformAtLeastR;
static bool mAttemptedLoad;
static std::mutex mLoadLock;
static struct stats_event_api_table* mStatsEventApi;
// static struct AStatsEvent_apiTable* mStatsEventApi;
static void* mStatsEventApi;
// non-static member variables
struct stats_event* mEventR = nullptr;
AStatsEvent* mEventR = nullptr;
stats_event_list mEventQ;
template <class T>

View File

@ -45,7 +45,7 @@ cc_library {
stubs: {
symbol_file: "libstatssocket.map.txt",
versions: [
"1",
"30",
],
}
}

View File

@ -17,14 +17,14 @@
#include "benchmark/benchmark.h"
#include "stats_event.h"
static struct stats_event* constructStatsEvent() {
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, 100);
static AStatsEvent* constructStatsEvent() {
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, 100);
// randomly sample atom size
int numElements = rand() % 800;
for (int i = 0; i < numElements; i++) {
stats_event_write_int32(event, i);
AStatsEvent_writeInt32(event, i);
}
return event;
@ -32,10 +32,10 @@ static struct stats_event* constructStatsEvent() {
static void BM_stats_event_truncate_buffer(benchmark::State& state) {
while (state.KeepRunning()) {
struct stats_event* event = constructStatsEvent();
stats_event_build(event);
stats_event_write(event);
stats_event_release(event);
AStatsEvent* event = constructStatsEvent();
AStatsEvent_build(event);
AStatsEvent_write(event);
AStatsEvent_release(event);
}
}
@ -43,11 +43,11 @@ BENCHMARK(BM_stats_event_truncate_buffer);
static void BM_stats_event_full_buffer(benchmark::State& state) {
while (state.KeepRunning()) {
struct stats_event* event = constructStatsEvent();
stats_event_truncate_buffer(event, false);
stats_event_build(event);
stats_event_write(event);
stats_event_release(event);
AStatsEvent* event = constructStatsEvent();
AStatsEvent_truncateBuffer(event, false);
AStatsEvent_build(event);
AStatsEvent_write(event);
AStatsEvent_release(event);
}
}

View File

@ -26,17 +26,17 @@
* This code defines and encapsulates the socket protocol.
*
* Usage:
* struct stats_event* event = stats_event_obtain();
* AStatsEvent* event = AStatsEvent_obtain();
*
* stats_event_set_atom_id(event, atomId);
* stats_event_write_int32(event, 24);
* stats_event_add_bool_annotation(event, 1, true); // annotations apply to the previous field
* stats_event_add_int32_annotation(event, 2, 128);
* stats_event_write_float(event, 2.0);
* AStatsEvent_setAtomId(event, atomId);
* AStatsEvent_writeInt32(event, 24);
* AStatsEvent_addBoolAnnotation(event, 1, true); // annotations apply to the previous field
* AStatsEvent_addInt32Annotation(event, 2, 128);
* AStatsEvent_writeFloat(event, 2.0);
*
* stats_event_build(event);
* stats_event_write(event);
* stats_event_release(event);
* AStatsEvent_build(event);
* AStatsEvent_write(event);
* AStatsEvent_release(event);
*
* Notes:
* (a) write_<type>() and add_<type>_annotation() should be called in the order that fields
@ -47,115 +47,118 @@
* (e) All strings should be encoded using UTF8.
*/
/* ERRORS */
#define ERROR_NO_TIMESTAMP 0x1
#define ERROR_NO_ATOM_ID 0x2
#define ERROR_OVERFLOW 0x4
#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
#define ERROR_INVALID_ANNOTATION_ID 0x40
#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
#define ERROR_TOO_MANY_ANNOTATIONS 0x100
#define ERROR_TOO_MANY_FIELDS 0x200
#define ERROR_INVALID_VALUE_TYPE 0x400
#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
/* TYPE IDS */
#define INT32_TYPE 0x00
#define INT64_TYPE 0x01
#define STRING_TYPE 0x02
#define LIST_TYPE 0x03
#define FLOAT_TYPE 0x04
#define BOOL_TYPE 0x05
#define BYTE_ARRAY_TYPE 0x06
#define OBJECT_TYPE 0x07
#define KEY_VALUE_PAIRS_TYPE 0x08
#define ATTRIBUTION_CHAIN_TYPE 0x09
#define ERROR_TYPE 0x0F
#ifdef __cplusplus
extern "C" {
#endif // __CPLUSPLUS
struct stats_event;
/* SYSTEM API */
struct stats_event* stats_event_obtain();
// The build function can be called multiple times without error. If the event
// has been built before, this function is a no-op.
void stats_event_build(struct stats_event* event);
int stats_event_write(struct stats_event* event);
void stats_event_release(struct stats_event* event);
void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId);
void stats_event_write_int32(struct stats_event* event, int32_t value);
void stats_event_write_int64(struct stats_event* event, int64_t value);
void stats_event_write_float(struct stats_event* event, float value);
void stats_event_write_bool(struct stats_event* event, bool value);
void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf, size_t numBytes);
// Buf must be null-terminated.
void stats_event_write_string8(struct stats_event* event, const char* value);
// Tags must be null-terminated.
void stats_event_write_attribution_chain(struct stats_event* event, const uint32_t* uids,
const char* const* tags, uint8_t numNodes);
/* key_value_pair struct can be constructed as follows:
* struct key_value_pair pair = {.key = key, .valueType = STRING_TYPE,
* .stringValue = buf};
/**
* Opaque struct use to represent a StatsEvent. It builds and stores the data that is sent to
* statsd.
*/
struct key_value_pair {
int32_t key;
uint8_t valueType; // expected to be INT32_TYPE, INT64_TYPE, FLOAT_TYPE, or STRING_TYPE
union {
int32_t int32Value;
int64_t int64Value;
float floatValue;
const char* stringValue; // must be null terminated
};
};
struct AStatsEvent;
typedef struct AStatsEvent AStatsEvent;
void stats_event_write_key_value_pairs(struct stats_event* event, struct key_value_pair* pairs,
uint8_t numPairs);
/**
* Returns a new AStatsEvent. If you call this function, you must call AStatsEvent_release to free
* the allocated memory.
*/
AStatsEvent* AStatsEvent_obtain();
void stats_event_add_bool_annotation(struct stats_event* event, uint8_t annotationId, bool value);
void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotationId,
int32_t value);
/**
* Builds and finalizes the StatsEvent.
*
* After this function, the StatsEvent must not be modified in any way other than calling release or
* write. Build must be always be called before AStatsEvent_write.
*
* Build can be called multiple times without error.
* If the event has been built before, this function is a no-op.
*/
void AStatsEvent_build(AStatsEvent* event);
uint32_t stats_event_get_atom_id(struct stats_event* event);
/**
* Writes the StatsEvent to the stats log.
*
* After calling this, AStatsEvent_release must be called,
* and is the only function that can be safely called.
*/
int AStatsEvent_write(AStatsEvent* event);
/**
* Frees the memory held by this StatsEvent
*
* After calling this, the StatsEvent must not be used or modified in any way.
*/
void AStatsEvent_release(AStatsEvent* event);
/**
* Sets the atom id for this StatsEvent.
**/
void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId);
/**
* Writes an int32_t field to this StatsEvent.
**/
void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value);
/**
* Writes an int64_t field to this StatsEvent.
**/
void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value);
/**
* Writes a float field to this StatsEvent.
**/
void AStatsEvent_writeFloat(AStatsEvent* event, float value);
/**
* Write a bool field to this StatsEvent.
**/
void AStatsEvent_writeBool(AStatsEvent* event, bool value);
/**
* Write a byte array field to this StatsEvent.
**/
void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes);
/**
* Write a string field to this StatsEvent.
*
* The string must be null-terminated.
**/
void AStatsEvent_writeString(AStatsEvent* event, const char* value);
/**
* Write an attribution chain field to this StatsEvent.
*
* The sizes of uids and tags must be equal. The AttributionNode at position i is
* made up of uids[i] and tags[i].
*
* \param uids array of uids in the attribution chain.
* \param tags array of tags in the attribution chain. Each tag must be null-terminated.
* \param numNodes the number of AttributionNodes in the attribution chain. This is the length of
* the uids and the tags.
**/
void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
const char* const* tags, uint8_t numNodes);
/**
* Write a bool annotation for the previous field written.
**/
void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value);
/**
* Write an integer annotation for the previous field written.
**/
void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value);
// Internal/test APIs. Should not be exposed outside of the APEX.
uint32_t AStatsEvent_getAtomId(AStatsEvent* event);
// Size is an output parameter.
uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size);
uint32_t stats_event_get_errors(struct stats_event* event);
// This table is used by StatsEventCompat to access the stats_event API.
struct stats_event_api_table {
struct stats_event* (*obtain)(void);
void (*build)(struct stats_event*);
int (*write)(struct stats_event*);
void (*release)(struct stats_event*);
void (*set_atom_id)(struct stats_event*, uint32_t);
void (*write_int32)(struct stats_event*, int32_t);
void (*write_int64)(struct stats_event*, int64_t);
void (*write_float)(struct stats_event*, float);
void (*write_bool)(struct stats_event*, bool);
void (*write_byte_array)(struct stats_event*, const uint8_t*, size_t);
void (*write_string8)(struct stats_event*, const char*);
void (*write_attribution_chain)(struct stats_event*, const uint32_t*, const char* const*,
uint8_t);
void (*write_key_value_pairs)(struct stats_event*, struct key_value_pair*, uint8_t);
void (*add_bool_annotation)(struct stats_event*, uint8_t, bool);
void (*add_int32_annotation)(struct stats_event*, uint8_t, int32_t);
uint32_t (*get_atom_id)(struct stats_event*);
uint8_t* (*get_buffer)(struct stats_event*, size_t*);
uint32_t (*get_errors)(struct stats_event*);
};
uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size);
uint32_t AStatsEvent_getErrors(AStatsEvent* event);
// exposed for benchmarking only
void stats_event_truncate_buffer(struct stats_event* event, bool truncate);
void AStatsEvent_truncateBuffer(struct AStatsEvent* event, bool truncate);
#ifdef __cplusplus
}

View File

@ -1,23 +1,19 @@
LIBSTATSSOCKET {
global:
stats_event_obtain; # apex # introduced=1
stats_event_build; # apex # introduced=1
stats_event_write; # apex # introduced=1
stats_event_release; # apex # introduced=1
stats_event_set_atom_id; # apex # introduced=1
stats_event_write_int32; # apex # introduced=1
stats_event_write_int64; # apex # introduced=1
stats_event_write_float; # apex # introduced=1
stats_event_write_bool; # apex # introduced=1
stats_event_write_byte_array; # apex # introduced=1
stats_event_write_string8; # apex # introduced=1
stats_event_write_attribution_chain; # apex # introduced=1
stats_event_write_key_value_pairs; # apex # introduced=1
stats_event_add_bool_annotation; # apex # introduced=1
stats_event_add_int32_annotation; # apex # introduced=1
stats_event_get_atom_id; # apex # introduced=1
stats_event_get_buffer; # apex # introduced=1
stats_event_get_errors; # apex # introduced=1
AStatsEvent_obtain; # apex # introduced=30
AStatsEvent_build; # apex # introduced=30
AStatsEvent_write; # apex # introduced=30
AStatsEvent_release; # apex # introduced=30
AStatsEvent_setAtomId; # apex # introduced=30
AStatsEvent_writeInt32; # apex # introduced=30
AStatsEvent_writeInt64; # apex # introduced=30
AStatsEvent_writeFloat; # apex # introduced=30
AStatsEvent_writeBool; # apex # introduced=30
AStatsEvent_writeByteArray; # apex # introduced=30
AStatsEvent_writeString; # apex # introduced=30
AStatsEvent_writeAttributionChain; # apex # introduced=30
AStatsEvent_addBoolAnnotation; # apex # introduced=30
AStatsEvent_addInt32Annotation; # apex # introduced=30
local:
*;
};

View File

@ -35,9 +35,36 @@
#define MAX_ANNOTATION_COUNT 15
#define MAX_BYTE_VALUE 127 // parsing side requires that lengths fit in 7 bits
// The stats_event struct holds the serialized encoding of an event
/* ERRORS */
#define ERROR_NO_TIMESTAMP 0x1
#define ERROR_NO_ATOM_ID 0x2
#define ERROR_OVERFLOW 0x4
#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
#define ERROR_INVALID_ANNOTATION_ID 0x40
#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
#define ERROR_TOO_MANY_ANNOTATIONS 0x100
#define ERROR_TOO_MANY_FIELDS 0x200
#define ERROR_INVALID_VALUE_TYPE 0x400
#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
/* TYPE IDS */
#define INT32_TYPE 0x00
#define INT64_TYPE 0x01
#define STRING_TYPE 0x02
#define LIST_TYPE 0x03
#define FLOAT_TYPE 0x04
#define BOOL_TYPE 0x05
#define BYTE_ARRAY_TYPE 0x06
#define OBJECT_TYPE 0x07
#define KEY_VALUE_PAIRS_TYPE 0x08
#define ATTRIBUTION_CHAIN_TYPE 0x09
#define ERROR_TYPE 0x0F
// The AStatsEvent struct holds the serialized encoding of an event
// within a buf. Also includes other required fields.
struct stats_event {
struct AStatsEvent {
uint8_t* buf;
size_t lastFieldPos; // location of last field within the buf
size_t size; // number of valid bytes within buffer
@ -55,8 +82,8 @@ static int64_t get_elapsed_realtime_ns() {
return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
}
struct stats_event* stats_event_obtain() {
struct stats_event* event = malloc(sizeof(struct stats_event));
AStatsEvent* AStatsEvent_obtain() {
AStatsEvent* event = malloc(sizeof(AStatsEvent));
event->buf = (uint8_t*)calloc(MAX_EVENT_PAYLOAD, 1);
event->buf[0] = OBJECT_TYPE;
event->atomId = 0;
@ -76,12 +103,12 @@ struct stats_event* stats_event_obtain() {
return event;
}
void stats_event_release(struct stats_event* event) {
void AStatsEvent_release(AStatsEvent* event) {
free(event->buf);
free(event);
}
void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId) {
void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
event->atomId = atomId;
event->buf[POS_ATOM_ID] = INT32_TYPE;
memcpy(&event->buf[POS_ATOM_ID + sizeof(uint8_t)], &atomId, sizeof(atomId));
@ -89,7 +116,7 @@ void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId) {
}
// Side-effect: modifies event->errors if the buffer would overflow
static bool overflows(struct stats_event* event, size_t size) {
static bool overflows(AStatsEvent* event, size_t size) {
if (event->size + size > MAX_EVENT_PAYLOAD) {
event->errors |= ERROR_OVERFLOW;
return true;
@ -99,39 +126,39 @@ static bool overflows(struct stats_event* event, size_t size) {
// Side-effect: all append functions increment event->size if there is
// sufficient space within the buffer to place the value
static void append_byte(struct stats_event* event, uint8_t value) {
static void append_byte(AStatsEvent* event, uint8_t value) {
if (!overflows(event, sizeof(value))) {
event->buf[event->size] = value;
event->size += sizeof(value);
}
}
static void append_bool(struct stats_event* event, bool value) {
static void append_bool(AStatsEvent* event, bool value) {
append_byte(event, (uint8_t)value);
}
static void append_int32(struct stats_event* event, int32_t value) {
static void append_int32(AStatsEvent* event, int32_t value) {
if (!overflows(event, sizeof(value))) {
memcpy(&event->buf[event->size], &value, sizeof(value));
event->size += sizeof(value);
}
}
static void append_int64(struct stats_event* event, int64_t value) {
static void append_int64(AStatsEvent* event, int64_t value) {
if (!overflows(event, sizeof(value))) {
memcpy(&event->buf[event->size], &value, sizeof(value));
event->size += sizeof(value);
}
}
static void append_float(struct stats_event* event, float value) {
static void append_float(AStatsEvent* event, float value) {
if (!overflows(event, sizeof(value))) {
memcpy(&event->buf[event->size], &value, sizeof(value));
event->size += sizeof(float);
}
}
static void append_byte_array(struct stats_event* event, const uint8_t* buf, size_t size) {
static void append_byte_array(AStatsEvent* event, const uint8_t* buf, size_t size) {
if (!overflows(event, size)) {
memcpy(&event->buf[event->size], buf, size);
event->size += size;
@ -139,7 +166,7 @@ static void append_byte_array(struct stats_event* event, const uint8_t* buf, siz
}
// Side-effect: modifies event->errors if buf is not properly null-terminated
static void append_string(struct stats_event* event, const char* buf) {
static void append_string(AStatsEvent* event, const char* buf) {
size_t size = strnlen(buf, MAX_EVENT_PAYLOAD);
if (size == MAX_EVENT_PAYLOAD) {
event->errors |= ERROR_STRING_NOT_NULL_TERMINATED;
@ -150,41 +177,41 @@ static void append_string(struct stats_event* event, const char* buf) {
append_byte_array(event, (uint8_t*)buf, size);
}
static void start_field(struct stats_event* event, uint8_t typeId) {
static void start_field(AStatsEvent* event, uint8_t typeId) {
event->lastFieldPos = event->size;
append_byte(event, typeId);
event->numElements++;
}
void stats_event_write_int32(struct stats_event* event, int32_t value) {
void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) {
if (event->errors) return;
start_field(event, INT32_TYPE);
append_int32(event, value);
}
void stats_event_write_int64(struct stats_event* event, int64_t value) {
void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) {
if (event->errors) return;
start_field(event, INT64_TYPE);
append_int64(event, value);
}
void stats_event_write_float(struct stats_event* event, float value) {
void AStatsEvent_writeFloat(AStatsEvent* event, float value) {
if (event->errors) return;
start_field(event, FLOAT_TYPE);
append_float(event, value);
}
void stats_event_write_bool(struct stats_event* event, bool value) {
void AStatsEvent_writeBool(AStatsEvent* event, bool value) {
if (event->errors) return;
start_field(event, BOOL_TYPE);
append_bool(event, value);
}
void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf, size_t numBytes) {
void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
if (event->errors) return;
start_field(event, BYTE_ARRAY_TYPE);
@ -193,7 +220,7 @@ void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf,
}
// Value is assumed to be encoded using UTF8
void stats_event_write_string8(struct stats_event* event, const char* value) {
void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
if (event->errors) return;
start_field(event, STRING_TYPE);
@ -201,8 +228,8 @@ void stats_event_write_string8(struct stats_event* event, const char* value) {
}
// Tags are assumed to be encoded using UTF8
void stats_event_write_attribution_chain(struct stats_event* event, const uint32_t* uids,
const char* const* tags, uint8_t numNodes) {
void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
const char* const* tags, uint8_t numNodes) {
if (numNodes > MAX_BYTE_VALUE) event->errors |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
if (event->errors) return;
@ -215,39 +242,8 @@ void stats_event_write_attribution_chain(struct stats_event* event, const uint32
}
}
void stats_event_write_key_value_pairs(struct stats_event* event, struct key_value_pair* pairs,
uint8_t numPairs) {
if (numPairs > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_KEY_VALUE_PAIRS;
if (event->errors) return;
start_field(event, KEY_VALUE_PAIRS_TYPE);
append_byte(event, numPairs);
for (uint8_t i = 0; i < numPairs; i++) {
append_int32(event, pairs[i].key);
append_byte(event, pairs[i].valueType);
switch (pairs[i].valueType) {
case INT32_TYPE:
append_int32(event, pairs[i].int32Value);
break;
case INT64_TYPE:
append_int64(event, pairs[i].int64Value);
break;
case FLOAT_TYPE:
append_float(event, pairs[i].floatValue);
break;
case STRING_TYPE:
append_string(event, pairs[i].stringValue);
break;
default:
event->errors |= ERROR_INVALID_VALUE_TYPE;
return;
}
}
}
// Side-effect: modifies event->errors if field has too many annotations
static void increment_annotation_count(struct stats_event* event) {
static void increment_annotation_count(AStatsEvent* event) {
uint8_t fieldType = event->buf[event->lastFieldPos] & 0x0F;
uint32_t oldAnnotationCount = (event->buf[event->lastFieldPos] & 0xF0) >> 4;
uint32_t newAnnotationCount = oldAnnotationCount + 1;
@ -260,7 +256,7 @@ static void increment_annotation_count(struct stats_event* event) {
event->buf[event->lastFieldPos] = (((uint8_t)newAnnotationCount << 4) & 0xF0) | fieldType;
}
void stats_event_add_bool_annotation(struct stats_event* event, uint8_t annotationId, bool value) {
void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) {
if (event->lastFieldPos == 0) event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
if (annotationId > MAX_BYTE_VALUE) event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
if (event->errors) return;
@ -271,8 +267,7 @@ void stats_event_add_bool_annotation(struct stats_event* event, uint8_t annotati
increment_annotation_count(event);
}
void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotationId,
int32_t value) {
void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) {
if (event->lastFieldPos == 0) event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
if (annotationId > MAX_BYTE_VALUE) event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
if (event->errors) return;
@ -283,24 +278,24 @@ void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotat
increment_annotation_count(event);
}
uint32_t stats_event_get_atom_id(struct stats_event* event) {
uint32_t AStatsEvent_getAtomId(AStatsEvent* event) {
return event->atomId;
}
uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size) {
uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size) {
if (size) *size = event->size;
return event->buf;
}
uint32_t stats_event_get_errors(struct stats_event* event) {
uint32_t AStatsEvent_getErrors(AStatsEvent* event) {
return event->errors;
}
void stats_event_truncate_buffer(struct stats_event* event, bool truncate) {
void AStatsEvent_truncateBuffer(AStatsEvent* event, bool truncate) {
event->truncate = truncate;
}
void stats_event_build(struct stats_event* event) {
void AStatsEvent_build(AStatsEvent* event) {
if (event->built) return;
if (event->atomId == 0) event->errors |= ERROR_NO_ATOM_ID;
@ -327,28 +322,7 @@ void stats_event_build(struct stats_event* event) {
event->built = true;
}
int stats_event_write(struct stats_event* event) {
stats_event_build(event);
int AStatsEvent_write(AStatsEvent* event) {
AStatsEvent_build(event);
return write_buffer_to_statsd(&event->buf, event->size, event->atomId);
}
struct stats_event_api_table table = {
stats_event_obtain,
stats_event_build,
stats_event_write,
stats_event_release,
stats_event_set_atom_id,
stats_event_write_int32,
stats_event_write_int64,
stats_event_write_float,
stats_event_write_bool,
stats_event_write_byte_array,
stats_event_write_string8,
stats_event_write_attribution_chain,
stats_event_write_key_value_pairs,
stats_event_add_bool_annotation,
stats_event_add_int32_annotation,
stats_event_get_atom_id,
stats_event_get_buffer,
stats_event_get_errors,
};
}

View File

@ -18,6 +18,34 @@
#include <gtest/gtest.h>
#include <utils/SystemClock.h>
// Keep in sync stats_event.c. Consider moving to separate header file to avoid duplication.
/* ERRORS */
#define ERROR_NO_TIMESTAMP 0x1
#define ERROR_NO_ATOM_ID 0x2
#define ERROR_OVERFLOW 0x4
#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
#define ERROR_INVALID_ANNOTATION_ID 0x40
#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
#define ERROR_TOO_MANY_ANNOTATIONS 0x100
#define ERROR_TOO_MANY_FIELDS 0x200
#define ERROR_INVALID_VALUE_TYPE 0x400
#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
/* TYPE IDS */
#define INT32_TYPE 0x00
#define INT64_TYPE 0x01
#define STRING_TYPE 0x02
#define LIST_TYPE 0x03
#define FLOAT_TYPE 0x04
#define BOOL_TYPE 0x05
#define BYTE_ARRAY_TYPE 0x06
#define OBJECT_TYPE 0x07
#define KEY_VALUE_PAIRS_TYPE 0x08
#define ATTRIBUTION_CHAIN_TYPE 0x09
#define ERROR_TYPE 0x0F
using std::string;
using std::vector;
@ -88,17 +116,17 @@ TEST(StatsEventTest, TestScalars) {
bool boolValue = false;
int64_t startTime = android::elapsedRealtimeNano();
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, atomId);
stats_event_write_int32(event, int32Value);
stats_event_write_int64(event, int64Value);
stats_event_write_float(event, floatValue);
stats_event_write_bool(event, boolValue);
stats_event_build(event);
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
AStatsEvent_writeInt32(event, int32Value);
AStatsEvent_writeInt64(event, int64Value);
AStatsEvent_writeFloat(event, floatValue);
AStatsEvent_writeBool(event, boolValue);
AStatsEvent_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
@ -120,8 +148,8 @@ TEST(StatsEventTest, TestScalars) {
checkScalar(&buffer, boolValue);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(stats_event_get_errors(event), 0);
stats_event_release(event);
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestStrings) {
@ -129,14 +157,14 @@ TEST(StatsEventTest, TestStrings) {
string str = "test_string";
int64_t startTime = android::elapsedRealtimeNano();
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, atomId);
stats_event_write_string8(event, str.c_str());
stats_event_build(event);
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
AStatsEvent_writeString(event, str.c_str());
AStatsEvent_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
@ -145,8 +173,8 @@ TEST(StatsEventTest, TestStrings) {
checkString(&buffer, str);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(stats_event_get_errors(event), 0);
stats_event_release(event);
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestByteArrays) {
@ -154,14 +182,14 @@ TEST(StatsEventTest, TestByteArrays) {
vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
int64_t startTime = android::elapsedRealtimeNano();
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, atomId);
stats_event_write_byte_array(event, message.data(), message.size());
stats_event_build(event);
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
AStatsEvent_writeByteArray(event, message.data(), message.size());
AStatsEvent_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
@ -170,8 +198,8 @@ TEST(StatsEventTest, TestByteArrays) {
checkByteArray(&buffer, message);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(stats_event_get_errors(event), 0);
stats_event_release(event);
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestAttributionChains) {
@ -188,14 +216,14 @@ TEST(StatsEventTest, TestAttributionChains) {
}
int64_t startTime = android::elapsedRealtimeNano();
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, atomId);
stats_event_write_attribution_chain(event, uids, cTags, numNodes);
stats_event_build(event);
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
AStatsEvent_writeAttributionChain(event, uids, cTags, numNodes);
AStatsEvent_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
@ -208,60 +236,8 @@ TEST(StatsEventTest, TestAttributionChains) {
}
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(stats_event_get_errors(event), 0);
stats_event_release(event);
}
TEST(StatsEventTest, TestKeyValuePairs) {
uint32_t atomId = 100;
uint8_t numPairs = 4;
struct key_value_pair pairs[numPairs];
pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = -1};
pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 5.5};
string str = "test_key_value_pair_string";
pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
int64_t startTime = android::elapsedRealtimeNano();
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, atomId);
stats_event_write_key_value_pairs(event, pairs, numPairs);
stats_event_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
checkTypeHeader(&buffer, KEY_VALUE_PAIRS_TYPE);
checkScalar(&buffer, numPairs);
// first pair
checkScalar(&buffer, pairs[0].key);
checkTypeHeader(&buffer, pairs[0].valueType);
checkScalar(&buffer, pairs[0].int32Value);
// second pair
checkScalar(&buffer, pairs[1].key);
checkTypeHeader(&buffer, pairs[1].valueType);
checkScalar(&buffer, pairs[1].int64Value);
// third pair
checkScalar(&buffer, pairs[2].key);
checkTypeHeader(&buffer, pairs[2].valueType);
checkScalar(&buffer, pairs[2].floatValue);
// fourth pair
checkScalar(&buffer, pairs[3].key);
checkTypeHeader(&buffer, pairs[3].valueType);
checkString(&buffer, str);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(stats_event_get_errors(event), 0);
stats_event_release(event);
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestAnnotations) {
@ -282,19 +258,19 @@ TEST(StatsEventTest, TestAnnotations) {
bool floatAnnotation2Value = false;
int64_t startTime = android::elapsedRealtimeNano();
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, 100);
stats_event_write_bool(event, boolValue);
stats_event_add_bool_annotation(event, boolAnnotation1Id, boolAnnotation1Value);
stats_event_add_int32_annotation(event, boolAnnotation2Id, boolAnnotation2Value);
stats_event_write_float(event, floatValue);
stats_event_add_int32_annotation(event, floatAnnotation1Id, floatAnnotation1Value);
stats_event_add_bool_annotation(event, floatAnnotation2Id, floatAnnotation2Value);
stats_event_build(event);
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, 100);
AStatsEvent_writeBool(event, boolValue);
AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value);
AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value);
AStatsEvent_writeFloat(event, floatValue);
AStatsEvent_addInt32Annotation(event, floatAnnotation1Id, floatAnnotation1Value);
AStatsEvent_addBoolAnnotation(event, floatAnnotation2Id, floatAnnotation2Value);
AStatsEvent_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
@ -312,33 +288,33 @@ TEST(StatsEventTest, TestAnnotations) {
checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(stats_event_get_errors(event), 0);
stats_event_release(event);
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestNoAtomIdError) {
struct stats_event* event = stats_event_obtain();
AStatsEvent* event = AStatsEvent_obtain();
// Don't set the atom id in order to trigger the error.
stats_event_build(event);
AStatsEvent_build(event);
uint32_t errors = stats_event_get_errors(event);
uint32_t errors = AStatsEvent_getErrors(event);
EXPECT_NE(errors | ERROR_NO_ATOM_ID, 0);
stats_event_release(event);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestOverflowError) {
struct stats_event* event = stats_event_obtain();
stats_event_set_atom_id(event, 100);
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, 100);
// Add 1000 int32s to the event. Each int32 takes 5 bytes so this will
// overflow the 4068 byte buffer.
for (int i = 0; i < 1000; i++) {
stats_event_write_int32(event, 0);
AStatsEvent_writeInt32(event, 0);
}
stats_event_build(event);
AStatsEvent_build(event);
uint32_t errors = stats_event_get_errors(event);
uint32_t errors = AStatsEvent_getErrors(event);
EXPECT_NE(errors | ERROR_OVERFLOW, 0);
stats_event_release(event);
AStatsEvent_release(event);
}