Merge "trusty: Helper library for metrics"
This commit is contained in:
commit
e23e85bee5
5 changed files with 492 additions and 0 deletions
51
trusty/metrics/Android.bp
Normal file
51
trusty/metrics/Android.bp
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright (C) 2021 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.
|
||||
|
||||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libtrusty_metrics",
|
||||
vendor: true,
|
||||
srcs: [
|
||||
"metrics.cpp",
|
||||
],
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libtrusty",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libtrusty_metrics_test",
|
||||
vendor: true,
|
||||
srcs: [
|
||||
"metrics_test.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libtrusty_metrics",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"liblog",
|
||||
"libtrusty",
|
||||
],
|
||||
require_root: true,
|
||||
}
|
57
trusty/metrics/include/trusty/metrics/metrics.h
Normal file
57
trusty/metrics/include/trusty/metrics/metrics.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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 <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/result.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
namespace android {
|
||||
namespace trusty {
|
||||
namespace metrics {
|
||||
|
||||
using android::base::Result;
|
||||
using android::base::unique_fd;
|
||||
|
||||
class TrustyMetrics {
|
||||
public:
|
||||
/* Wait for next event with a given timeout. Negative timeout means infinite timeout. */
|
||||
Result<void> WaitForEvent(int timeout_ms = -1);
|
||||
/* Attempt to handle an event from Metrics TA in a non-blocking manner. */
|
||||
Result<void> HandleEvent();
|
||||
/* Expose TIPC channel so that client can integrate it into an event loop with other fds. */
|
||||
int GetRawFd() { return metrics_fd_; };
|
||||
|
||||
protected:
|
||||
TrustyMetrics(std::string tipc_dev) : tipc_dev_(std::move(tipc_dev)), metrics_fd_(-1) {}
|
||||
virtual ~TrustyMetrics(){};
|
||||
|
||||
Result<void> Open();
|
||||
virtual void HandleCrash(const std::string& app_id) = 0;
|
||||
virtual void HandleEventDrop() = 0;
|
||||
|
||||
private:
|
||||
std::string tipc_dev_;
|
||||
unique_fd metrics_fd_;
|
||||
};
|
||||
|
||||
} // namespace metrics
|
||||
} // namespace trusty
|
||||
} // namespace android
|
103
trusty/metrics/include/trusty/metrics/tipc.h
Normal file
103
trusty/metrics/include/trusty/metrics/tipc.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2021, 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 <stdint.h>
|
||||
|
||||
/**
|
||||
* DOC: Metrics
|
||||
*
|
||||
* Metrics interface provides a way for Android to get Trusty metrics data.
|
||||
*
|
||||
* Currently, only "push" model is supported. Clients are expected to connect to
|
||||
* metrics service, listen for events, e.g. app crash events, and respond to
|
||||
* every event with a &struct metrics_req.
|
||||
*
|
||||
* Communication is driven by metrics service, i.e. requests/responses are all
|
||||
* sent from/to metrics service.
|
||||
*
|
||||
* Note that the type of the event is not known to the client ahead of time.
|
||||
*
|
||||
* In the future, if we need to have Android "pull" metrics data from Trusty,
|
||||
* that can be done by introducing a separate port.
|
||||
*
|
||||
* This interface is shared between Android and Trusty. There is a copy in each
|
||||
* repository. They must be kept in sync.
|
||||
*/
|
||||
|
||||
#define METRICS_PORT "com.android.trusty.metrics"
|
||||
|
||||
/**
|
||||
* enum metrics_cmd - command identifiers for metrics interface
|
||||
* @METRICS_CMD_RESP_BIT: message is a response
|
||||
* @METRICS_CMD_REQ_SHIFT: number of bits used by @METRICS_CMD_RESP_BIT
|
||||
* @METRICS_CMD_REPORT_EVENT_DROP: report gaps in the event stream
|
||||
* @METRICS_CMD_REPORT_CRASH: report an app crash event
|
||||
*/
|
||||
enum metrics_cmd {
|
||||
METRICS_CMD_RESP_BIT = 1,
|
||||
METRICS_CMD_REQ_SHIFT = 1,
|
||||
|
||||
METRICS_CMD_REPORT_EVENT_DROP = (1 << METRICS_CMD_REQ_SHIFT),
|
||||
METRICS_CMD_REPORT_CRASH = (2 << METRICS_CMD_REQ_SHIFT),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum metrics_error - metrics error codes
|
||||
* @METRICS_NO_ERROR: no error
|
||||
* @METRICS_ERR_UNKNOWN_CMD: unknown or not implemented command
|
||||
*/
|
||||
enum metrics_error {
|
||||
METRICS_NO_ERROR = 0,
|
||||
METRICS_ERR_UNKNOWN_CMD = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct metrics_req - common structure for metrics requests
|
||||
* @cmd: command identifier - one of &enum metrics_cmd
|
||||
* @reserved: must be 0
|
||||
*/
|
||||
struct metrics_req {
|
||||
uint32_t cmd;
|
||||
uint32_t reserved;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* struct metrics_resp - common structure for metrics responses
|
||||
* @cmd: command identifier - %METRICS_CMD_RESP_BIT or'ed with a cmd in
|
||||
* one of &enum metrics_cmd
|
||||
* @status: response status, one of &enum metrics_error
|
||||
*/
|
||||
struct metrics_resp {
|
||||
uint32_t cmd;
|
||||
uint32_t status;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* struct metrics_report_crash_req - arguments of %METRICS_CMD_REPORT_CRASH
|
||||
* requests
|
||||
* @app_id_len: length of app ID that follows this structure
|
||||
*/
|
||||
struct metrics_report_crash_req {
|
||||
uint32_t app_id_len;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define METRICS_MAX_APP_ID_LEN 256
|
||||
|
||||
#define METRICS_MAX_MSG_SIZE \
|
||||
(sizeof(struct metrics_req) + sizeof(struct metrics_report_crash_req) + \
|
||||
METRICS_MAX_APP_ID_LEN)
|
142
trusty/metrics/metrics.cpp
Normal file
142
trusty/metrics/metrics.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Sourete 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "metrics"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <trusty/metrics/metrics.h>
|
||||
#include <trusty/metrics/tipc.h>
|
||||
#include <trusty/tipc.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace android {
|
||||
namespace trusty {
|
||||
namespace metrics {
|
||||
|
||||
using android::base::ErrnoError;
|
||||
using android::base::Error;
|
||||
|
||||
Result<void> TrustyMetrics::Open() {
|
||||
int fd = tipc_connect(tipc_dev_.c_str(), METRICS_PORT);
|
||||
if (fd < 0) {
|
||||
return ErrnoError() << "failed to connect to Trusty metrics TA";
|
||||
}
|
||||
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0) {
|
||||
return ErrnoError() << "failed F_GETFL";
|
||||
}
|
||||
|
||||
int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (rc < 0) {
|
||||
return ErrnoError() << "failed F_SETFL";
|
||||
}
|
||||
|
||||
metrics_fd_.reset(fd);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> TrustyMetrics::WaitForEvent(int timeout_ms) {
|
||||
if (!metrics_fd_.ok()) {
|
||||
return Error() << "connection to Metrics TA has not been initialized yet";
|
||||
}
|
||||
|
||||
struct pollfd pfd = {
|
||||
.fd = metrics_fd_,
|
||||
.events = POLLIN,
|
||||
};
|
||||
|
||||
int rc = poll(&pfd, 1, timeout_ms);
|
||||
if (rc != 1) {
|
||||
return ErrnoError() << "failed poll()";
|
||||
}
|
||||
|
||||
if (!(pfd.revents & POLLIN)) {
|
||||
return ErrnoError() << "channel not ready";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> TrustyMetrics::HandleEvent() {
|
||||
if (!metrics_fd_.ok()) {
|
||||
return Error() << "connection to Metrics TA has not been initialized yet";
|
||||
}
|
||||
|
||||
uint8_t msg[METRICS_MAX_MSG_SIZE];
|
||||
|
||||
auto rc = read(metrics_fd_, msg, sizeof(msg));
|
||||
if (rc < 0) {
|
||||
return ErrnoError() << "failed to read metrics message";
|
||||
}
|
||||
size_t msg_len = rc;
|
||||
|
||||
if (msg_len < sizeof(metrics_req)) {
|
||||
return Error() << "message too small: " << rc;
|
||||
}
|
||||
auto req = reinterpret_cast<metrics_req*>(msg);
|
||||
size_t offset = sizeof(metrics_req);
|
||||
uint32_t status = METRICS_NO_ERROR;
|
||||
|
||||
switch (req->cmd) {
|
||||
case METRICS_CMD_REPORT_CRASH: {
|
||||
if (msg_len < offset + sizeof(metrics_report_crash_req)) {
|
||||
return Error() << "message too small: " << rc;
|
||||
}
|
||||
auto crash_args = reinterpret_cast<metrics_report_crash_req*>(msg + offset);
|
||||
offset += sizeof(metrics_report_crash_req);
|
||||
|
||||
if (msg_len < offset + crash_args->app_id_len) {
|
||||
return Error() << "message too small: " << rc;
|
||||
}
|
||||
auto app_id_ptr = reinterpret_cast<char*>(msg + offset);
|
||||
std::string app_id(app_id_ptr, crash_args->app_id_len);
|
||||
|
||||
HandleCrash(app_id);
|
||||
break;
|
||||
}
|
||||
|
||||
case METRICS_CMD_REPORT_EVENT_DROP:
|
||||
HandleEventDrop();
|
||||
break;
|
||||
|
||||
default:
|
||||
status = METRICS_ERR_UNKNOWN_CMD;
|
||||
break;
|
||||
}
|
||||
|
||||
metrics_resp resp = {
|
||||
.cmd = req->cmd | METRICS_CMD_RESP_BIT,
|
||||
.status = status,
|
||||
};
|
||||
|
||||
rc = write(metrics_fd_, &resp, sizeof(resp));
|
||||
if (rc < 0) {
|
||||
return ErrnoError() << "failed to request next metrics event";
|
||||
}
|
||||
|
||||
if (rc != (int)sizeof(resp)) {
|
||||
return Error() << "unexpected number of bytes sent event: " << rc;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace metrics
|
||||
} // namespace trusty
|
||||
} // namespace android
|
139
trusty/metrics/metrics_test.cpp
Normal file
139
trusty/metrics/metrics_test.cpp
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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 <android-base/unique_fd.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <poll.h>
|
||||
#include <trusty/metrics/metrics.h>
|
||||
#include <trusty/tipc.h>
|
||||
|
||||
#define TIPC_DEV "/dev/trusty-ipc-dev0"
|
||||
#define CRASHER_PORT "com.android.trusty.metrics.test.crasher"
|
||||
|
||||
namespace android {
|
||||
namespace trusty {
|
||||
namespace metrics {
|
||||
|
||||
using android::base::unique_fd;
|
||||
|
||||
static void TriggerCrash() {
|
||||
size_t num_retries = 3;
|
||||
int fd = -1;
|
||||
|
||||
for (size_t i = 0; i < num_retries; i++) {
|
||||
/* It's possible to time out waiting for crasher TA to restart. */
|
||||
fd = tipc_connect(TIPC_DEV, CRASHER_PORT);
|
||||
if (fd >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unique_fd crasher(fd);
|
||||
ASSERT_GE(crasher, 0);
|
||||
|
||||
int msg = 0;
|
||||
int rc = write(crasher, &msg, sizeof(msg));
|
||||
ASSERT_EQ(rc, sizeof(msg));
|
||||
}
|
||||
|
||||
class TrustyMetricsTest : public TrustyMetrics, public ::testing::Test {
|
||||
public:
|
||||
TrustyMetricsTest() : TrustyMetrics(TIPC_DEV) {}
|
||||
|
||||
virtual void HandleCrash(const std::string& app_id) override { crashed_app_ = app_id; }
|
||||
|
||||
virtual void HandleEventDrop() override { event_drop_count_++; }
|
||||
|
||||
virtual void SetUp() override {
|
||||
auto ret = Open();
|
||||
ASSERT_TRUE(ret.ok()) << ret.error();
|
||||
}
|
||||
|
||||
void WaitForAndHandleEvent() {
|
||||
auto ret = WaitForEvent(30000 /* 30 second timeout */);
|
||||
ASSERT_TRUE(ret.ok()) << ret.error();
|
||||
|
||||
ret = HandleEvent();
|
||||
ASSERT_TRUE(ret.ok()) << ret.error();
|
||||
}
|
||||
|
||||
std::string crashed_app_;
|
||||
size_t event_drop_count_;
|
||||
};
|
||||
|
||||
TEST_F(TrustyMetricsTest, Crash) {
|
||||
TriggerCrash();
|
||||
WaitForAndHandleEvent();
|
||||
|
||||
/* Check that correct TA crashed. */
|
||||
ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
|
||||
}
|
||||
|
||||
TEST_F(TrustyMetricsTest, PollSet) {
|
||||
int binder_fd;
|
||||
int rc = IPCThreadState::self()->setupPolling(&binder_fd);
|
||||
ASSERT_EQ(rc, 0);
|
||||
ASSERT_GE(binder_fd, 0);
|
||||
|
||||
TriggerCrash();
|
||||
|
||||
struct pollfd pfds[] = {
|
||||
{
|
||||
.fd = binder_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
{
|
||||
.fd = GetRawFd(),
|
||||
.events = POLLIN,
|
||||
},
|
||||
};
|
||||
|
||||
rc = poll(pfds, 2, 30000 /* 30 second timeout */);
|
||||
/* We expect one event on the metrics fd. */
|
||||
ASSERT_EQ(rc, 1);
|
||||
ASSERT_TRUE(pfds[1].revents & POLLIN);
|
||||
|
||||
auto ret = HandleEvent();
|
||||
ASSERT_TRUE(ret.ok()) << ret.error();
|
||||
|
||||
/* Check that correct TA crashed. */
|
||||
ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
|
||||
}
|
||||
|
||||
TEST_F(TrustyMetricsTest, EventDrop) {
|
||||
/* We know the size of the internal event queue is less than this. */
|
||||
size_t num_events = 3;
|
||||
|
||||
ASSERT_EQ(event_drop_count_, 0);
|
||||
|
||||
for (auto i = 0; i < num_events; i++) {
|
||||
TriggerCrash();
|
||||
}
|
||||
|
||||
for (auto i = 0; i < num_events; i++) {
|
||||
WaitForAndHandleEvent();
|
||||
if (event_drop_count_ > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(event_drop_count_, 1);
|
||||
}
|
||||
|
||||
} // namespace metrics
|
||||
} // namespace trusty
|
||||
} // namespace android
|
Loading…
Reference in a new issue