Migrate to packages/modules/StatsD/lib/*

BUG: 167962588
Test: TH
Merged-In: I22db7e344a9a96bfc16e009624b4896625306e83
Change-Id: I0c4e4aaece738259bafe57d420dfd1c941f834de
Exempt-From-Owner-Approval: Code Migration / Cleanup
This commit is contained in:
Baligh Uddin 2020-12-10 13:21:37 +00:00
parent f96f8e8c7f
commit 6ae4f777bc
22 changed files with 0 additions and 2456 deletions

View file

@ -1,111 +0,0 @@
//
// 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.
//
// ==========================================================
// Native library to register a pull atom callback with statsd
// ==========================================================
cc_defaults {
name: "libstatspull_defaults",
srcs: [
"stats_pull_atom_callback.cpp",
],
cflags: [
"-Wall",
"-Werror",
],
export_include_dirs: ["include"],
shared_libs: [
"libbinder_ndk",
"liblog",
"libstatssocket",
],
static_libs: [
"libutils",
"statsd-aidl-ndk_platform",
],
}
cc_library_shared {
name: "libstatspull",
defaults: [
"libstatspull_defaults"
],
// enumerate stable entry points for APEX use
stubs: {
symbol_file: "libstatspull.map.txt",
versions: [
"30",
],
},
apex_available: [
"com.android.os.statsd",
"test_com.android.os.statsd",
],
stl: "libc++_static",
// TODO(b/151102177): Enable it when the build error is fixed.
header_abi_checker: {
enabled: false,
},
}
// ONLY USE IN TESTS.
cc_library_static {
name: "libstatspull_private",
defaults: [
"libstatspull_defaults",
],
visibility: [
"//frameworks/base/apex/statsd/tests/libstatspull",
"//packages/modules/StatsD/apex/tests/libstatspull",
],
}
// Note: These unit tests only test PullAtomMetadata.
// For full E2E tests of libstatspull, use LibStatsPullTests
cc_test {
name: "libstatspull_test",
srcs: [
"tests/pull_atom_metadata_test.cpp",
],
shared_libs: [
"libstatspull",
"libstatssocket",
],
test_suites: ["general-tests", "mts"],
test_config: "libstatspull_test.xml",
//TODO(b/153588990): Remove when the build system properly separates
//32bit and 64bit architectures.
compile_multilib: "both",
multilib: {
lib64: {
suffix: "64",
},
lib32: {
suffix: "32",
},
},
cflags: [
"-Wall",
"-Werror",
"-Wno-missing-field-initializers",
"-Wno-unused-variable",
"-Wno-unused-function",
"-Wno-unused-parameter",
],
require_root: true,
}

View file

@ -1,7 +0,0 @@
joeo@google.com
muhammadq@google.com
ruchirr@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
yro@google.com

View file

@ -1,7 +0,0 @@
{
"presubmit" : [
{
"name" : "libstatspull_test"
}
]
}

View file

@ -1,170 +0,0 @@
/*
* 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.
*/
#pragma once
#include <stats_event.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Opaque struct representing the metadata for registering an AStatsManager_PullAtomCallback.
*/
struct AStatsManager_PullAtomMetadata;
typedef struct AStatsManager_PullAtomMetadata AStatsManager_PullAtomMetadata;
/**
* Allocate and initialize new PullAtomMetadata.
*
* Must call AStatsManager_PullAtomMetadata_release to free the memory.
*/
AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain();
/**
* 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 milliseconds. If two successive pulls are issued
* within the cool down, a cached version of the first will be used for the second. The minimum
* allowed cool down is one second.
*/
void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
int64_t cool_down_millis);
/**
* Get the cool down time of the pull in milliseconds.
*/
int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata);
/**
* Set the maximum time the pull can take in milliseconds.
* The maximum allowed timeout is 10 seconds.
*/
void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
int64_t timeout_millis);
/**
* Get the maximum time the pull can take in milliseconds.
*/
int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata);
/**
* 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,
int32_t* additive_fields, int32_t num_fields);
/**
* Get the number of additive fields for this pulled atom. This is intended to be called before
* AStatsManager_PullAtomMetadata_getAdditiveFields to determine the size of the array.
*/
int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
AStatsManager_PullAtomMetadata* metadata);
/**
* Get the additive fields of this pulled atom.
*
* \param fields an output parameter containing the additive fields for this PullAtomMetadata.
* Fields is an array and it is assumed that it is at least as large as the number of
* additive fields, which can be obtained by calling
* AStatsManager_PullAtomMetadata_getNumAdditiveFields.
*/
void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
int32_t* fields);
/**
* Return codes for the result of a pull.
*/
typedef int32_t AStatsManager_PullAtomCallbackReturn;
enum {
// 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,
};
/**
* Opaque struct representing a list of AStatsEvent objects.
*/
struct AStatsEventList;
typedef struct AStatsEventList AStatsEventList;
/**
* 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);
/**
* 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);
/**
* Sets 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.
*
* Requires the REGISTER_STATS_PULL_ATOM permission.
*
* \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_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
AStatsManager_PullAtomCallback callback, void* cookie);
/**
* Clears a callback for an atom when that atom is to be pulled. Note that any ongoing
* pulls will still occur.
*
* Requires the REGISTER_STATS_PULL_ATOM permission.
*
* \param atomTag The tag of the atom of which to unregister
*/
void AStatsManager_clearPullAtomCallback(int32_t atom_tag);
#ifdef __cplusplus
}
#endif

View file

@ -1,17 +0,0 @@
LIBSTATSPULL {
global:
AStatsManager_PullAtomMetadata_obtain; # apex # introduced=30
AStatsManager_PullAtomMetadata_release; # apex # introduced=30
AStatsManager_PullAtomMetadata_setCoolDownMillis; # apex # introduced=30
AStatsManager_PullAtomMetadata_getCoolDownMillis; # apex # introduced=30
AStatsManager_PullAtomMetadata_setTimeoutMillis; # apex # introduced=30
AStatsManager_PullAtomMetadata_getTimeoutMillis; # apex # introduced=30
AStatsManager_PullAtomMetadata_setAdditiveFields; # apex # introduced=30
AStatsManager_PullAtomMetadata_getNumAdditiveFields; # apex # introduced=30
AStatsManager_PullAtomMetadata_getAdditiveFields; # apex # introduced=30
AStatsEventList_addStatsEvent; # apex # introduced=30
AStatsManager_setPullAtomCallback; # apex # introduced=30
AStatsManager_clearPullAtomCallback; # apex # introduced=30
local:
*;
};

View file

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.
-->
<configuration description="Runs libstatspull_test.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<option name="test-suite-tag" value="mts" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="libstatspull_test->/data/local/tmp/libstatspull_test" />
<option name="append-bitness" value="true" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="libstatspull_test" />
</test>
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="mainline-module-package-name" value="com.google.android.os.statsd" />
</object>
</configuration>

View file

@ -1,260 +0,0 @@
/*
* 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.
*/
#include <map>
#include <thread>
#include <vector>
#include <stats_event.h>
#include <stats_pull_atom_callback.h>
#include <aidl/android/os/BnPullAtomCallback.h>
#include <aidl/android/os/IPullAtomResultReceiver.h>
#include <aidl/android/os/IStatsd.h>
#include <aidl/android/util/StatsEventParcel.h>
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
using Status = ::ndk::ScopedAStatus;
using aidl::android::os::BnPullAtomCallback;
using aidl::android::os::IPullAtomResultReceiver;
using aidl::android::os::IStatsd;
using aidl::android::util::StatsEventParcel;
using ::ndk::SharedRefBase;
struct AStatsEventList {
std::vector<AStatsEvent*> data;
};
AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {
AStatsEvent* event = AStatsEvent_obtain();
pull_data->data.push_back(event);
return event;
}
static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL; // 1 second.
static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL; // 2 seconds.
struct AStatsManager_PullAtomMetadata {
int64_t cool_down_millis;
int64_t timeout_millis;
std::vector<int32_t> additive_fields;
};
AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {
AStatsManager_PullAtomMetadata* metadata = new AStatsManager_PullAtomMetadata();
metadata->cool_down_millis = DEFAULT_COOL_DOWN_MILLIS;
metadata->timeout_millis = DEFAULT_TIMEOUT_MILLIS;
metadata->additive_fields = std::vector<int32_t>();
return metadata;
}
void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {
delete metadata;
}
void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
int64_t cool_down_millis) {
metadata->cool_down_millis = cool_down_millis;
}
int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) {
return metadata->cool_down_millis;
}
void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
int64_t timeout_millis) {
metadata->timeout_millis = timeout_millis;
}
int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) {
return metadata->timeout_millis;
}
void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
int32_t* additive_fields,
int32_t num_fields) {
metadata->additive_fields.assign(additive_fields, additive_fields + num_fields);
}
int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
AStatsManager_PullAtomMetadata* metadata) {
return metadata->additive_fields.size();
}
void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
int32_t* fields) {
std::copy(metadata->additive_fields.begin(), metadata->additive_fields.end(), fields);
}
class StatsPullAtomCallbackInternal : public BnPullAtomCallback {
public:
StatsPullAtomCallbackInternal(const AStatsManager_PullAtomCallback callback, void* cookie,
const int64_t coolDownMillis, const int64_t timeoutMillis,
const std::vector<int32_t> additiveFields)
: mCallback(callback),
mCookie(cookie),
mCoolDownMillis(coolDownMillis),
mTimeoutMillis(timeoutMillis),
mAdditiveFields(additiveFields) {}
Status onPullAtom(int32_t atomTag,
const std::shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
AStatsEventList statsEventList;
int successInt = mCallback(atomTag, &statsEventList, mCookie);
bool success = successInt == AStatsManager_PULL_SUCCESS;
// Convert stats_events into StatsEventParcels.
std::vector<StatsEventParcel> parcels;
for (int i = 0; i < statsEventList.data.size(); i++) {
size_t size;
uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
// stats_event.h/c uses a vector as opposed to a buffer.
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
}
Status status = resultReceiver->pullFinished(atomTag, success, parcels);
if (!status.isOk()) {
std::vector<StatsEventParcel> emptyParcels;
resultReceiver->pullFinished(atomTag, /*success=*/false, emptyParcels);
}
for (int i = 0; i < statsEventList.data.size(); i++) {
AStatsEvent_release(statsEventList.data[i]);
}
return Status::ok();
}
int64_t getCoolDownMillis() const { return mCoolDownMillis; }
int64_t getTimeoutMillis() const { return mTimeoutMillis; }
const std::vector<int32_t>& getAdditiveFields() const { return mAdditiveFields; }
private:
const AStatsManager_PullAtomCallback mCallback;
void* mCookie;
const int64_t mCoolDownMillis;
const int64_t mTimeoutMillis;
const std::vector<int32_t> mAdditiveFields;
};
static std::mutex pullAtomMutex;
static std::shared_ptr<IStatsd> sStatsd = nullptr;
static std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> mPullers;
static std::shared_ptr<IStatsd> getStatsService();
static void binderDied(void* /*cookie*/) {
{
std::lock_guard<std::mutex> lock(pullAtomMutex);
sStatsd = nullptr;
}
std::shared_ptr<IStatsd> statsService = getStatsService();
if (statsService == nullptr) {
return;
}
// Since we do not want to make an IPC with the lock held, we first create a
// copy of the data with the lock held before iterating through the map.
std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> pullersCopy;
{
std::lock_guard<std::mutex> lock(pullAtomMutex);
pullersCopy = mPullers;
}
for (const auto& it : pullersCopy) {
statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownMillis(),
it.second->getTimeoutMillis(),
it.second->getAdditiveFields(), it.second);
}
}
static ::ndk::ScopedAIBinder_DeathRecipient sDeathRecipient(
AIBinder_DeathRecipient_new(binderDied));
static std::shared_ptr<IStatsd> getStatsService() {
std::lock_guard<std::mutex> lock(pullAtomMutex);
if (!sStatsd) {
// Fetch statsd
::ndk::SpAIBinder binder(AServiceManager_getService("stats"));
sStatsd = IStatsd::fromBinder(binder);
if (sStatsd) {
AIBinder_linkToDeath(binder.get(), sDeathRecipient.get(), /*cookie=*/nullptr);
}
}
return sStatsd;
}
void registerStatsPullAtomCallbackBlocking(int32_t atomTag,
std::shared_ptr<StatsPullAtomCallbackInternal> cb) {
const std::shared_ptr<IStatsd> statsService = getStatsService();
if (statsService == nullptr) {
// Statsd not available
return;
}
statsService->registerNativePullAtomCallback(
atomTag, cb->getCoolDownMillis(), cb->getTimeoutMillis(), cb->getAdditiveFields(), cb);
}
void unregisterStatsPullAtomCallbackBlocking(int32_t atomTag) {
const std::shared_ptr<IStatsd> statsService = getStatsService();
if (statsService == nullptr) {
// Statsd not available
return;
}
statsService->unregisterNativePullAtomCallback(atomTag);
}
void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
AStatsManager_PullAtomCallback callback, void* cookie) {
int64_t coolDownMillis =
metadata == nullptr ? DEFAULT_COOL_DOWN_MILLIS : metadata->cool_down_millis;
int64_t timeoutMillis = metadata == nullptr ? DEFAULT_TIMEOUT_MILLIS : metadata->timeout_millis;
std::vector<int32_t> additiveFields;
if (metadata != nullptr) {
additiveFields = metadata->additive_fields;
}
std::shared_ptr<StatsPullAtomCallbackInternal> callbackBinder =
SharedRefBase::make<StatsPullAtomCallbackInternal>(callback, cookie, coolDownMillis,
timeoutMillis, additiveFields);
{
std::lock_guard<std::mutex> lg(pullAtomMutex);
// Always add to the map. If statsd is dead, we will add them when it comes back.
mPullers[atom_tag] = callbackBinder;
}
std::thread registerThread(registerStatsPullAtomCallbackBlocking, atom_tag, callbackBinder);
registerThread.detach();
}
void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
{
std::lock_guard<std::mutex> lg(pullAtomMutex);
// Always remove the puller from our map.
// If statsd is down, we will not register it when it comes back.
mPullers.erase(atom_tag);
}
std::thread unregisterThread(unregisterStatsPullAtomCallbackBlocking, atom_tag);
unregisterThread.detach();
}

View file

@ -1,92 +0,0 @@
/*
* Copyright (C) 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <stats_pull_atom_callback.h>
namespace {
static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL; // 1 second.
static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL; // 2 seconds.
} // anonymous namespace
TEST(AStatsManager_PullAtomMetadataTest, TestEmpty) {
AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
AStatsManager_PullAtomMetadata_release(metadata);
}
TEST(AStatsManager_PullAtomMetadataTest, TestSetTimeoutMillis) {
int64_t timeoutMillis = 500;
AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, timeoutMillis);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), timeoutMillis);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
AStatsManager_PullAtomMetadata_release(metadata);
}
TEST(AStatsManager_PullAtomMetadataTest, TestSetCoolDownMillis) {
int64_t coolDownMillis = 10000;
AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, coolDownMillis);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), coolDownMillis);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
AStatsManager_PullAtomMetadata_release(metadata);
}
TEST(AStatsManager_PullAtomMetadataTest, TestSetAdditiveFields) {
const int numFields = 3;
int inputFields[numFields] = {2, 4, 6};
AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, inputFields, numFields);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), numFields);
int outputFields[numFields];
AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, outputFields);
for (int i = 0; i < numFields; i++) {
EXPECT_EQ(inputFields[i], outputFields[i]);
}
AStatsManager_PullAtomMetadata_release(metadata);
}
TEST(AStatsManager_PullAtomMetadataTest, TestSetAllElements) {
int64_t timeoutMillis = 500;
int64_t coolDownMillis = 10000;
const int numFields = 3;
int inputFields[numFields] = {2, 4, 6};
AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, timeoutMillis);
AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, coolDownMillis);
AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, inputFields, numFields);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), coolDownMillis);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), timeoutMillis);
EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), numFields);
int outputFields[numFields];
AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, outputFields);
for (int i = 0; i < numFields; i++) {
EXPECT_EQ(inputFields[i], outputFields[i]);
}
AStatsManager_PullAtomMetadata_release(metadata);
}

View file

@ -1,127 +0,0 @@
//
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// =========================================================================
// Native library to write stats log to statsd socket on Android R and later
// =========================================================================
cc_defaults {
name: "libstatssocket_defaults",
srcs: [
"stats_buffer_writer.c",
"stats_event.c",
"stats_socket.c",
"statsd_writer.c",
],
export_include_dirs: ["include"],
static_libs: [
"libcutils", // does not expose a stable C API
],
header_libs: ["liblog_headers"],
cflags: [
"-Wall",
"-Werror",
],
}
cc_library {
name: "libstatssocket",
defaults: [
"libstatssocket_defaults",
],
host_supported: true,
target: {
// On android, libstatssocket should only be linked as a shared lib
android: {
static: {
enabled: false,
},
},
host: {
shared: {
enabled: false,
},
},
},
stl: "libc++_static",
// enumerate stable entry points for APEX use
stubs: {
symbol_file: "libstatssocket.map.txt",
versions: [
"30",
],
},
apex_available: [
"com.android.os.statsd",
"test_com.android.os.statsd",
],
}
//TODO (b/149842105): Figure out if there is a better solution for this.
cc_test_library {
name: "libstatssocket_private",
defaults: [
"libstatssocket_defaults",
],
visibility: [
"//frameworks/base/apex/statsd/tests/libstatspull",
"//frameworks/base/cmds/statsd",
"//packages/modules/StatsD/apex/tests/libstatspull",
"//packages/modules/StatsD/bin",
],
}
cc_library_headers {
name: "libstatssocket_headers",
export_include_dirs: ["include"],
host_supported: true,
apex_available: ["com.android.resolv"],
min_sdk_version: "29",
}
cc_test {
name: "libstatssocket_test",
srcs: [
"tests/stats_event_test.cpp",
"tests/stats_writer_test.cpp",
],
cflags: [
"-Wall",
"-Werror",
],
static_libs: [
"libgmock",
"libstatssocket_private",
],
shared_libs: [
"libcutils",
"libutils",
],
test_suites: ["device-tests", "mts"],
test_config: "libstatssocket_test.xml",
//TODO(b/153588990): Remove when the build system properly separates.
//32bit and 64bit architectures.
compile_multilib: "both",
multilib: {
lib64: {
suffix: "64",
},
lib32: {
suffix: "32",
},
},
require_root: true,
}

View file

@ -1,7 +0,0 @@
{
"presubmit" : [
{
"name" : "libstatssocket_test"
}
]
}

View file

@ -1,30 +0,0 @@
/*
* 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.
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif // __CPLUSPLUS
void stats_log_close();
int stats_log_is_closed();
int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId);
#ifdef __cplusplus
}
#endif // __CPLUSPLUS

View file

@ -1,164 +0,0 @@
/*
* 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.
*/
#ifndef ANDROID_STATS_LOG_STATS_EVENT_H
#define ANDROID_STATS_LOG_STATS_EVENT_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/*
* Functionality to build and store the buffer sent over the statsd socket.
* This code defines and encapsulates the socket protocol.
*
* Usage:
* AStatsEvent* event = AStatsEvent_obtain();
*
* AStatsEvent_setAtomId(event, atomId);
* AStatsEvent_addBoolAnnotation(event, 5, false); // atom-level annotation
* AStatsEvent_writeInt32(event, 24);
* AStatsEvent_addBoolAnnotation(event, 1, true); // annotation for preceding atom field
* AStatsEvent_addInt32Annotation(event, 2, 128);
* AStatsEvent_writeFloat(event, 2.0);
*
* AStatsEvent_write(event);
* AStatsEvent_release(event);
*
* Note that calls to add atom fields and annotations should be made in the
* order that they are defined in the atom.
*/
#ifdef __cplusplus
extern "C" {
#endif // __CPLUSPLUS
/**
* Opaque struct use to represent a StatsEvent. It builds and stores the data that is sent to
* statsd.
*/
struct AStatsEvent;
typedef struct AStatsEvent AStatsEvent;
/**
* Returns a new AStatsEvent. If you call this function, you must call AStatsEvent_release to free
* the allocated memory.
*/
AStatsEvent* AStatsEvent_obtain();
/**
* Builds and finalizes the AStatsEvent for a pulled event.
* This should only be called for pulled AStatsEvents.
*
* After this function, the StatsEvent must not be modified in any way other than calling release or
* 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);
/**
* 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.
*
* This function should be called immediately after AStatsEvent_obtain. It may
* be called additional times as well, but subsequent calls will have no effect.
**/
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.
void AStatsEvent_overwriteTimestamp(AStatsEvent* event, uint64_t timestampNs);
uint32_t AStatsEvent_getAtomId(AStatsEvent* event);
// Size is an output parameter.
uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size);
uint32_t AStatsEvent_getErrors(AStatsEvent* event);
#ifdef __cplusplus
}
#endif // __CPLUSPLUS
#endif // ANDROID_STATS_LOG_STATS_EVENT_H

View file

@ -1,33 +0,0 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
/**
* Helpers to manage the statsd socket.
**/
#ifdef __cplusplus
extern "C" {
#endif // __CPLUSPLUS
/**
* Closes the statsd socket file descriptor.
**/
void AStatsSocket_close();
#ifdef __cplusplus
}
#endif // __CPLUSPLUS

View file

@ -1,20 +0,0 @@
LIBSTATSSOCKET {
global:
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
AStatsSocket_close; # apex # introduced=30
local:
*;
};

View file

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.
-->
<configuration description="Runs libstatssocket_test.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<option name="test-suite-tag" value="mts" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="libstatssocket_test->/data/local/tmp/libstatssocket_test" />
<option name="append-bitness" value="true" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="libstatssocket_test" />
</test>
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="mainline-module-package-name" value="com.google.android.os.statsd" />
</object>
</configuration>

View file

@ -1,126 +0,0 @@
/*
* 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.
*/
#include "include/stats_buffer_writer.h"
#ifdef __ANDROID__
#include <cutils/properties.h>
#endif
#include <errno.h>
#include <sys/time.h>
#include <sys/uio.h>
#include "statsd_writer.h"
static const uint32_t kStatsEventTag = 1937006964;
extern struct android_log_transport_write statsdLoggerWrite;
static int __write_to_statsd_init(struct iovec* vec, size_t nr);
static int (*__write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
void note_log_drop(int error, int atomId) {
statsdLoggerWrite.noteDrop(error, atomId);
}
void stats_log_close() {
statsd_writer_init_lock();
__write_to_statsd = __write_to_statsd_init;
if (statsdLoggerWrite.close) {
(*statsdLoggerWrite.close)();
}
statsd_writer_init_unlock();
}
int stats_log_is_closed() {
return statsdLoggerWrite.isClosed && (*statsdLoggerWrite.isClosed)();
}
int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
int ret = 1;
struct iovec vecs[2];
vecs[0].iov_base = (void*)&kStatsEventTag;
vecs[0].iov_len = sizeof(kStatsEventTag);
vecs[1].iov_base = buffer;
vecs[1].iov_len = size;
ret = __write_to_statsd(vecs, 2);
if (ret < 0) {
note_log_drop(ret, atomId);
}
return ret;
}
static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
int save_errno;
struct timespec ts;
size_t len, i;
for (len = i = 0; i < nr; ++i) {
len += vec[i].iov_len;
}
if (!len) {
return -EINVAL;
}
save_errno = errno;
#if defined(__ANDROID__)
clock_gettime(CLOCK_REALTIME, &ts);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
#endif
int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
errno = save_errno;
return ret;
}
static int __write_to_statsd_initialize_locked() {
if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
if (statsdLoggerWrite.close) {
(*statsdLoggerWrite.close)();
return -ENODEV;
}
}
return 1;
}
static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
int ret, save_errno = errno;
statsd_writer_init_lock();
if (__write_to_statsd == __write_to_statsd_init) {
ret = __write_to_statsd_initialize_locked();
if (ret < 0) {
statsd_writer_init_unlock();
errno = save_errno;
return ret;
}
__write_to_statsd = __write_to_stats_daemon;
}
statsd_writer_init_unlock();
ret = __write_to_statsd(vec, nr);
errno = save_errno;
return ret;
}

View file

@ -1,351 +0,0 @@
/*
* 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.
*/
#include "include/stats_event.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "stats_buffer_writer.h"
#define LOGGER_ENTRY_MAX_PAYLOAD 4068
// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
// See android_util_Stats_Log.cpp
#define MAX_PUSH_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - 4)
#define MAX_PULL_EVENT_PAYLOAD (50 * 1024) // 50 KB
/* POSITIONS */
#define POS_NUM_ELEMENTS 1
#define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t))
#define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t))
/* LIMITS */
#define MAX_ANNOTATION_COUNT 15
#define MAX_BYTE_VALUE 127 // parsing side requires that lengths fit in 7 bits
/* 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
#define ERROR_ATOM_ID_INVALID_POSITION 0x2000
/* 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 AStatsEvent {
uint8_t* buf;
// Location of last field within the buf. Here, field denotes either a
// metadata field (e.g. timestamp) or an atom field.
size_t lastFieldPos;
// Number of valid bytes within the buffer.
size_t numBytesWritten;
uint32_t numElements;
uint32_t atomId;
uint32_t errors;
bool built;
size_t bufSize;
};
static int64_t get_elapsed_realtime_ns() {
struct timespec t;
t.tv_sec = t.tv_nsec = 0;
clock_gettime(CLOCK_BOOTTIME, &t);
return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
}
AStatsEvent* AStatsEvent_obtain() {
AStatsEvent* event = malloc(sizeof(AStatsEvent));
event->lastFieldPos = 0;
event->numBytesWritten = 2; // reserve first 2 bytes for root event type and number of elements
event->numElements = 0;
event->atomId = 0;
event->errors = 0;
event->built = false;
event->bufSize = MAX_PUSH_EVENT_PAYLOAD;
event->buf = (uint8_t*)calloc(event->bufSize, 1);
event->buf[0] = OBJECT_TYPE;
AStatsEvent_writeInt64(event, get_elapsed_realtime_ns()); // write the timestamp
return event;
}
void AStatsEvent_release(AStatsEvent* event) {
free(event->buf);
free(event);
}
void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
if (event->atomId != 0) return;
if (event->numElements != 1) {
event->errors |= ERROR_ATOM_ID_INVALID_POSITION;
return;
}
event->atomId = atomId;
AStatsEvent_writeInt32(event, atomId);
}
// Overwrites the timestamp populated in AStatsEvent_obtain with a custom
// timestamp. Should only be called from test code.
void AStatsEvent_overwriteTimestamp(AStatsEvent* event, uint64_t timestampNs) {
memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], &timestampNs, sizeof(timestampNs));
// Do not increment numElements because we already accounted for the timestamp
// within AStatsEvent_obtain.
}
// Side-effect: modifies event->errors if the buffer would overflow
static bool overflows(AStatsEvent* event, size_t size) {
const size_t totalBytesNeeded = event->numBytesWritten + size;
if (totalBytesNeeded > MAX_PULL_EVENT_PAYLOAD) {
event->errors |= ERROR_OVERFLOW;
return true;
}
// Expand buffer if needed.
if (event->bufSize < MAX_PULL_EVENT_PAYLOAD && totalBytesNeeded > event->bufSize) {
do {
event->bufSize *= 2;
} while (event->bufSize <= totalBytesNeeded);
if (event->bufSize > MAX_PULL_EVENT_PAYLOAD) {
event->bufSize = MAX_PULL_EVENT_PAYLOAD;
}
event->buf = (uint8_t*)realloc(event->buf, event->bufSize);
}
return false;
}
// Side-effect: all append functions increment event->numBytesWritten if there is
// sufficient space within the buffer to place the value
static void append_byte(AStatsEvent* event, uint8_t value) {
if (!overflows(event, sizeof(value))) {
event->buf[event->numBytesWritten] = value;
event->numBytesWritten += sizeof(value);
}
}
static void append_bool(AStatsEvent* event, bool value) {
append_byte(event, (uint8_t)value);
}
static void append_int32(AStatsEvent* event, int32_t value) {
if (!overflows(event, sizeof(value))) {
memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
event->numBytesWritten += sizeof(value);
}
}
static void append_int64(AStatsEvent* event, int64_t value) {
if (!overflows(event, sizeof(value))) {
memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
event->numBytesWritten += sizeof(value);
}
}
static void append_float(AStatsEvent* event, float value) {
if (!overflows(event, sizeof(value))) {
memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
event->numBytesWritten += sizeof(float);
}
}
static void append_byte_array(AStatsEvent* event, const uint8_t* buf, size_t size) {
if (!overflows(event, size)) {
memcpy(&event->buf[event->numBytesWritten], buf, size);
event->numBytesWritten += size;
}
}
// Side-effect: modifies event->errors if buf is not properly null-terminated
static void append_string(AStatsEvent* event, const char* buf) {
size_t size = strnlen(buf, MAX_PULL_EVENT_PAYLOAD);
if (size == MAX_PULL_EVENT_PAYLOAD) {
event->errors |= ERROR_STRING_NOT_NULL_TERMINATED;
return;
}
append_int32(event, size);
append_byte_array(event, (uint8_t*)buf, size);
}
static void start_field(AStatsEvent* event, uint8_t typeId) {
event->lastFieldPos = event->numBytesWritten;
append_byte(event, typeId);
event->numElements++;
}
void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) {
start_field(event, INT32_TYPE);
append_int32(event, value);
}
void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) {
start_field(event, INT64_TYPE);
append_int64(event, value);
}
void AStatsEvent_writeFloat(AStatsEvent* event, float value) {
start_field(event, FLOAT_TYPE);
append_float(event, value);
}
void AStatsEvent_writeBool(AStatsEvent* event, bool value) {
start_field(event, BOOL_TYPE);
append_bool(event, value);
}
void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
start_field(event, BYTE_ARRAY_TYPE);
append_int32(event, numBytes);
append_byte_array(event, buf, numBytes);
}
// Value is assumed to be encoded using UTF8
void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
start_field(event, STRING_TYPE);
append_string(event, value);
}
// Tags are assumed to be encoded using UTF8
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;
return;
}
start_field(event, ATTRIBUTION_CHAIN_TYPE);
append_byte(event, numNodes);
for (uint8_t i = 0; i < numNodes; i++) {
append_int32(event, uids[i]);
append_string(event, tags[i]);
}
}
// Side-effect: modifies event->errors if field has too many annotations
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;
if (newAnnotationCount > MAX_ANNOTATION_COUNT) {
event->errors |= ERROR_TOO_MANY_ANNOTATIONS;
return;
}
event->buf[event->lastFieldPos] = (((uint8_t)newAnnotationCount << 4) & 0xF0) | fieldType;
}
void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) {
if (event->numElements < 2) {
event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
return;
} else if (annotationId > MAX_BYTE_VALUE) {
event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
return;
}
append_byte(event, annotationId);
append_byte(event, BOOL_TYPE);
append_bool(event, value);
increment_annotation_count(event);
}
void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) {
if (event->numElements < 2) {
event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
return;
} else if (annotationId > MAX_BYTE_VALUE) {
event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
return;
}
append_byte(event, annotationId);
append_byte(event, INT32_TYPE);
append_int32(event, value);
increment_annotation_count(event);
}
uint32_t AStatsEvent_getAtomId(AStatsEvent* event) {
return event->atomId;
}
uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size) {
if (size) *size = event->numBytesWritten;
return event->buf;
}
uint32_t AStatsEvent_getErrors(AStatsEvent* event) {
return event->errors;
}
static void build_internal(AStatsEvent* event, const bool push) {
if (event->numElements > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_FIELDS;
if (0 == event->atomId) event->errors |= ERROR_NO_ATOM_ID;
if (push && event->numBytesWritten > MAX_PUSH_EVENT_PAYLOAD) event->errors |= ERROR_OVERFLOW;
// If there are errors, rewrite buffer.
if (event->errors) {
// Discard everything after the atom id (including atom-level
// annotations). This leaves only two elements (timestamp and atom id).
event->numElements = 2;
// Reset number of atom-level annotations to 0.
event->buf[POS_ATOM_ID] = INT32_TYPE;
// Now, write errors to the buffer immediately after the atom id.
event->numBytesWritten = POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t);
start_field(event, ERROR_TYPE);
append_int32(event, event->errors);
}
event->buf[POS_NUM_ELEMENTS] = event->numElements;
}
void AStatsEvent_build(AStatsEvent* event) {
if (event->built) return;
build_internal(event, false /* push */);
event->built = true;
}
int AStatsEvent_write(AStatsEvent* event) {
build_internal(event, true /* push */);
return write_buffer_to_statsd(event->buf, event->numBytesWritten, event->atomId);
}

View file

@ -1,22 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "include/stats_socket.h"
#include "stats_buffer_writer.h"
void AStatsSocket_close() {
stats_log_close();
}

View file

@ -1,294 +0,0 @@
/*
* Copyright (C) 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "statsd_writer.h"
#include <cutils/fs.h>
#include <cutils/sockets.h>
#include <cutils/threads.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include <stdarg.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int dropped = 0;
static atomic_int log_error = 0;
static atomic_int atom_tag = 0;
void statsd_writer_init_lock() {
/*
* If we trigger a signal handler in the middle of locked activity and the
* signal handler logs a message, we could get into a deadlock state.
*/
pthread_mutex_lock(&log_init_lock);
}
int statd_writer_trylock() {
return pthread_mutex_trylock(&log_init_lock);
}
void statsd_writer_init_unlock() {
pthread_mutex_unlock(&log_init_lock);
}
static int statsdAvailable();
static int statsdOpen();
static void statsdClose();
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
static void statsdNoteDrop();
static int statsdIsClosed();
struct android_log_transport_write statsdLoggerWrite = {
.name = "statsd",
.sock = -EBADF,
.available = statsdAvailable,
.open = statsdOpen,
.close = statsdClose,
.write = statsdWrite,
.noteDrop = statsdNoteDrop,
.isClosed = statsdIsClosed,
};
/* log_init_lock assumed */
static int statsdOpen() {
int i, ret = 0;
i = atomic_load(&statsdLoggerWrite.sock);
if (i < 0) {
int flags = SOCK_DGRAM;
#ifdef SOCK_CLOEXEC
flags |= SOCK_CLOEXEC;
#endif
#ifdef SOCK_NONBLOCK
flags |= SOCK_NONBLOCK;
#endif
int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
if (sock < 0) {
ret = -errno;
} else {
int sndbuf = 1 * 1024 * 1024; // set max send buffer size 1MB
socklen_t bufLen = sizeof(sndbuf);
// SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.
// Proceed to connect even setsockopt fails.
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, bufLen);
struct sockaddr_un un;
memset(&un, 0, sizeof(struct sockaddr_un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/statsdw");
if (TEMP_FAILURE_RETRY(
connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
ret = -errno;
switch (ret) {
case -ENOTCONN:
case -ECONNREFUSED:
case -ENOENT:
i = atomic_exchange(&statsdLoggerWrite.sock, ret);
/* FALLTHRU */
default:
break;
}
close(sock);
} else {
ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
if ((ret >= 0) && (ret != sock)) {
close(ret);
}
ret = 0;
}
}
}
return ret;
}
static void __statsdClose(int negative_errno) {
int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
if (sock >= 0) {
close(sock);
}
}
static void statsdClose() {
__statsdClose(-EBADF);
}
static int statsdAvailable() {
if (atomic_load(&statsdLoggerWrite.sock) < 0) {
if (access("/dev/socket/statsdw", W_OK) == 0) {
return 0;
}
return -EBADF;
}
return 1;
}
static void statsdNoteDrop(int error, int tag) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
}
static int statsdIsClosed() {
if (atomic_load(&statsdLoggerWrite.sock) < 0) {
return 1;
}
return 0;
}
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
ssize_t ret;
int sock;
static const unsigned headerLength = 1;
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
sock = atomic_load(&statsdLoggerWrite.sock);
if (sock < 0) switch (sock) {
case -ENOTCONN:
case -ECONNREFUSED:
case -ENOENT:
break;
default:
return -EBADF;
}
/*
* struct {
* // what we provide to socket
* android_log_header_t header;
* // caller provides
* union {
* struct {
* char prio;
* char payload[];
* } string;
* struct {
* uint32_t tag
* char payload[];
* } binary;
* };
* };
*/
header.tid = gettid();
header.realtime.tv_sec = ts->tv_sec;
header.realtime.tv_nsec = ts->tv_nsec;
newVec[0].iov_base = (unsigned char*)&header;
newVec[0].iov_len = sizeof(header);
// If we dropped events before, try to tell statsd.
if (sock >= 0) {
int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
if (snapshot) {
android_log_event_long_t buffer;
header.id = LOG_ID_STATS;
// store the last log error in the tag field. This tag field is not used by statsd.
buffer.header.tag = atomic_load(&log_error);
buffer.payload.type = EVENT_TYPE_LONG;
// format:
// |atom_tag|dropped_count|
int64_t composed_long = atomic_load(&atom_tag);
// Send 2 int32's via an int64.
composed_long = ((composed_long << 32) | ((int64_t)snapshot));
buffer.payload.data = composed_long;
newVec[headerLength].iov_base = &buffer;
newVec[headerLength].iov_len = sizeof(buffer);
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
}
}
}
header.id = LOG_ID_STATS;
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
newVec[i].iov_base = vec[i - headerLength].iov_base;
payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
if (newVec[i].iov_len) {
++i;
}
break;
}
}
/*
* The write below could be lost, but will never block.
*
* ENOTCONN occurs if statsd has died.
* ENOENT occurs if statsd is not running and socket is missing.
* ECONNREFUSED occurs if we can not reconnect to statsd.
* EAGAIN occurs if statsd is overloaded.
*/
if (sock < 0) {
ret = sock;
} else {
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
if (ret < 0) {
ret = -errno;
}
}
switch (ret) {
case -ENOTCONN:
case -ECONNREFUSED:
case -ENOENT:
if (statd_writer_trylock()) {
return ret; /* in a signal handler? try again when less stressed
*/
}
__statsdClose(ret);
ret = statsdOpen();
statsd_writer_init_unlock();
if (ret < 0) {
return ret;
}
ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
if (ret < 0) {
ret = -errno;
}
/* FALLTHRU */
default:
break;
}
if (ret > (ssize_t)sizeof(header)) {
ret -= sizeof(header);
}
return ret;
}

View file

@ -1,47 +0,0 @@
/*
* Copyright (C) 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_STATS_LOG_STATS_WRITER_H
#define ANDROID_STATS_LOG_STATS_WRITER_H
#include <pthread.h>
#include <stdatomic.h>
#include <sys/socket.h>
/**
* Internal lock should not be exposed. This is bad design.
* TODO: rewrite it in c++ code and encapsulate the functionality in a
* StatsdWriter class.
*/
void statsd_writer_init_lock();
int statsd_writer_init_trylock();
void statsd_writer_init_unlock();
struct android_log_transport_write {
const char* name; /* human name to describe the transport */
atomic_int sock;
int (*available)(); /* Does not cause resources to be taken */
int (*open)(); /* can be called multiple times, reusing current resources */
void (*close)(); /* free up resources */
/* write log to transport, returns number of bytes propagated, or -errno */
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
/* note one log drop */
void (*noteDrop)(int error, int tag);
/* checks if the socket is closed */
int (*isClosed)();
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H

View file

@ -1,460 +0,0 @@
/*
* 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.
*/
#include "stats_event.h"
#include <gtest/gtest.h>
#include <utils/SystemClock.h>
// Keep in sync with 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
#define ERROR_ATOM_ID_INVALID_POSITION 0x2000
/* 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;
// Side-effect: this function moves the start of the buffer past the read value
template <class T>
T readNext(uint8_t** buffer) {
T value;
if ((reinterpret_cast<uintptr_t>(*buffer) % alignof(T)) == 0) {
value = *(T*)(*buffer);
} else {
memcpy(&value, *buffer, sizeof(T));
}
*buffer += sizeof(T);
return value;
}
void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
uint8_t typeHeader = (numAnnotations << 4) | typeId;
EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
}
template <class T>
void checkScalar(uint8_t** buffer, T expectedValue) {
EXPECT_EQ(readNext<T>(buffer), expectedValue);
}
void checkString(uint8_t** buffer, const string& expectedString) {
uint32_t size = readNext<uint32_t>(buffer);
string parsedString((char*)(*buffer), size);
EXPECT_EQ(parsedString, expectedString);
*buffer += size; // move buffer past string we just read
}
void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
uint32_t size = readNext<uint32_t>(buffer);
vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
EXPECT_EQ(parsedByteArray, expectedByteArray);
*buffer += size; // move buffer past byte array we just read
}
template <class T>
void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
checkScalar<T>(buffer, annotationValue);
}
void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) {
// All events start with OBJECT_TYPE id.
checkTypeHeader(buffer, OBJECT_TYPE);
// We increment by 2 because the number of elements listed in the
// serialization accounts for the timestamp and atom id as well.
checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
// Check timestamp
checkTypeHeader(buffer, INT64_TYPE);
int64_t timestamp = readNext<int64_t>(buffer);
EXPECT_GE(timestamp, startTime);
EXPECT_LE(timestamp, endTime);
// Check atom id
checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations);
checkScalar(buffer, atomId);
}
TEST(StatsEventTest, TestScalars) {
uint32_t atomId = 100;
int32_t int32Value = -5;
int64_t int64Value = -2 * android::elapsedRealtimeNano();
float floatValue = 2.0;
bool boolValue = false;
int64_t startTime = android::elapsedRealtimeNano();
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 = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
// check int32 element
checkTypeHeader(&buffer, INT32_TYPE);
checkScalar(&buffer, int32Value);
// check int64 element
checkTypeHeader(&buffer, INT64_TYPE);
checkScalar(&buffer, int64Value);
// check float element
checkTypeHeader(&buffer, FLOAT_TYPE);
checkScalar(&buffer, floatValue);
// check bool element
checkTypeHeader(&buffer, BOOL_TYPE);
checkScalar(&buffer, boolValue);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestStrings) {
uint32_t atomId = 100;
string str = "test_string";
int64_t startTime = android::elapsedRealtimeNano();
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 = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
checkTypeHeader(&buffer, STRING_TYPE);
checkString(&buffer, str);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestByteArrays) {
uint32_t atomId = 100;
vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
int64_t startTime = android::elapsedRealtimeNano();
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 = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
checkByteArray(&buffer, message);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestAttributionChains) {
uint32_t atomId = 100;
uint8_t numNodes = 50;
uint32_t uids[numNodes];
vector<string> tags(numNodes); // storage that cTag elements point to
const char* cTags[numNodes];
for (int i = 0; i < (int)numNodes; i++) {
uids[i] = i;
tags.push_back("test" + std::to_string(i));
cTags[i] = tags[i].c_str();
}
int64_t startTime = android::elapsedRealtimeNano();
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 = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
checkScalar(&buffer, numNodes);
for (int i = 0; i < numNodes; i++) {
checkScalar(&buffer, uids[i]);
checkString(&buffer, tags[i]);
}
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestFieldAnnotations) {
uint32_t atomId = 100;
// first element information
bool boolValue = false;
uint8_t boolAnnotation1Id = 1;
uint8_t boolAnnotation2Id = 2;
bool boolAnnotation1Value = true;
int32_t boolAnnotation2Value = 3;
// second element information
float floatValue = -5.0;
uint8_t floatAnnotation1Id = 3;
uint8_t floatAnnotation2Id = 4;
int32_t floatAnnotation1Value = 8;
bool floatAnnotation2Value = false;
int64_t startTime = android::elapsedRealtimeNano();
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
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 = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
// check first element
checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
checkScalar(&buffer, boolValue);
checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
// check second element
checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
checkScalar(&buffer, floatValue);
checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestAtomLevelAnnotations) {
uint32_t atomId = 100;
// atom-level annotation information
uint8_t boolAnnotationId = 1;
uint8_t int32AnnotationId = 2;
bool boolAnnotationValue = false;
int32_t int32AnnotationValue = 5;
float fieldValue = -3.5;
int64_t startTime = android::elapsedRealtimeNano();
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue);
AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue);
AStatsEvent_writeFloat(event, fieldValue);
AStatsEvent_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId,
/*numAtomLevelAnnotations=*/2);
// check atom-level annotations
checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue);
checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue);
// check first element
checkTypeHeader(&buffer, FLOAT_TYPE);
checkScalar(&buffer, fieldValue);
EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestNoAtomIdError) {
AStatsEvent* event = AStatsEvent_obtain();
// Don't set the atom id in order to trigger the error.
AStatsEvent_build(event);
uint32_t errors = AStatsEvent_getErrors(event);
EXPECT_EQ(errors & ERROR_NO_ATOM_ID, ERROR_NO_ATOM_ID);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestPushOverflowError) {
const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const int writeCount = 120; // Number of times to write str in the event.
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, 100);
// Add str to the event 120 times. Each str takes >35 bytes so this will
// overflow the 4068 byte buffer.
// We want to keep writeCount less than 127 to avoid hitting
// ERROR_TOO_MANY_FIELDS.
for (int i = 0; i < writeCount; i++) {
AStatsEvent_writeString(event, str);
}
AStatsEvent_write(event);
uint32_t errors = AStatsEvent_getErrors(event);
EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestPullOverflowError) {
const uint32_t atomId = 10100;
const vector<uint8_t> bytes(430 /* number of elements */, 1 /* value of each element */);
const int writeCount = 120; // Number of times to write bytes in the event.
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
// Add bytes to the event 120 times. Size of bytes is 430 so this will
// overflow the 50 KB pulled event buffer.
// We want to keep writeCount less than 127 to avoid hitting
// ERROR_TOO_MANY_FIELDS.
for (int i = 0; i < writeCount; i++) {
AStatsEvent_writeByteArray(event, bytes.data(), bytes.size());
}
AStatsEvent_build(event);
uint32_t errors = AStatsEvent_getErrors(event);
EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestLargePull) {
const uint32_t atomId = 100;
const string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const int writeCount = 120; // Number of times to write str in the event.
const int64_t startTime = android::elapsedRealtimeNano();
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
// Add str to the event 120 times.
// We want to keep writeCount less than 127 to avoid hitting
// ERROR_TOO_MANY_FIELDS.
for (int i = 0; i < writeCount; i++) {
AStatsEvent_writeString(event, str.c_str());
}
AStatsEvent_build(event);
int64_t endTime = android::elapsedRealtimeNano();
size_t bufferSize;
uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
uint8_t* bufferEnd = buffer + bufferSize;
checkMetadata(&buffer, writeCount, startTime, endTime, atomId);
// Check all instances of str have been written.
for (int i = 0; i < writeCount; i++) {
checkTypeHeader(&buffer, STRING_TYPE);
checkString(&buffer, str);
}
EXPECT_EQ(buffer, bufferEnd); // Ensure that we have read the entire buffer.
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestAtomIdInvalidPositionError) {
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_writeInt32(event, 0);
AStatsEvent_setAtomId(event, 100);
AStatsEvent_writeBool(event, true);
AStatsEvent_build(event);
uint32_t errors = AStatsEvent_getErrors(event);
EXPECT_EQ(errors & ERROR_ATOM_ID_INVALID_POSITION, ERROR_ATOM_ID_INVALID_POSITION);
AStatsEvent_release(event);
}
TEST(StatsEventTest, TestOverwriteTimestamp) {
uint32_t atomId = 100;
int64_t expectedTimestamp = 0x123456789;
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomId);
AStatsEvent_overwriteTimestamp(event, expectedTimestamp);
AStatsEvent_build(event);
uint8_t* buffer = AStatsEvent_getBuffer(event, NULL);
// Make sure that the timestamp is being overwritten.
checkMetadata(&buffer, /*numElements=*/0, /*startTime=*/expectedTimestamp,
/*endTime=*/expectedTimestamp, atomId);
EXPECT_EQ(AStatsEvent_getErrors(event), 0);
AStatsEvent_release(event);
}

View file

@ -1,36 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "stats_buffer_writer.h"
#include "stats_event.h"
#include "stats_socket.h"
TEST(StatsWriterTest, TestSocketClose) {
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, 100);
AStatsEvent_writeInt32(event, 5);
int successResult = AStatsEvent_write(event);
AStatsEvent_release(event);
// In the case of a successful write, we return the number of bytes written.
EXPECT_GT(successResult, 0);
EXPECT_FALSE(stats_log_is_closed());
AStatsSocket_close();
EXPECT_TRUE(stats_log_is_closed());
}