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:
parent
f96f8e8c7f
commit
6ae4f777bc
22 changed files with 0 additions and 2456 deletions
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"presubmit" : [
|
||||
{
|
||||
"name" : "libstatspull_test"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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:
|
||||
*;
|
||||
};
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"presubmit" : [
|
||||
{
|
||||
"name" : "libstatssocket_test"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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:
|
||||
*;
|
||||
};
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)], ×tampNs, 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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue