diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index e0222d9f036de..b085a09a86084 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -66,7 +66,8 @@ statsd_common_src := \ src/subscriber/IncidentdReporter.cpp \ src/subscriber/SubscriberReporter.cpp \ src/HashableDimensionKey.cpp \ - src/guardrail/StatsdStats.cpp + src/guardrail/StatsdStats.cpp \ + src/socket/StatsSocketListener.cpp statsd_common_c_includes := \ $(LOCAL_PATH)/src \ @@ -96,7 +97,10 @@ statsd_common_shared_libraries := \ android.hardware.health@2.0 \ android.hardware.power@1.0 \ android.hardware.power@1.1 \ - android.hardware.thermal@1.0 + android.hardware.thermal@1.0 \ + libpackagelistparser \ + libsysutils \ + libcutils # ========= # statsd diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index 8ce9ec7bc7d85..e8904c6253251 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -14,10 +14,12 @@ * limitations under the License. */ +#define DEBUG false // STOPSHIP if true #include "Log.h" #include "StatsService.h" #include "logd/LogReader.h" +#include "socket/StatsSocketListener.h" #include #include @@ -35,7 +37,9 @@ using namespace android; using namespace android::os::statsd; -// ================================================================================ +const bool kUseLogd = false; +const bool kUseStatsdSocket = true; + /** * Thread function data. */ @@ -48,12 +52,8 @@ struct log_reader_thread_data { */ static void* log_reader_thread_func(void* cookie) { log_reader_thread_data* data = static_cast(cookie); - sp reader = new LogReader(data->service); - // Tell StatsService that we're ready to go. - data->service->Startup(); - // Run the read loop. Never returns. reader->Run(); @@ -96,10 +96,7 @@ static status_t start_log_reader_thread(const sp& service) { return NO_ERROR; } -// ================================================================================ int main(int /*argc*/, char** /*argv*/) { - status_t err; - // Set up the looper sp looper(Looper::prepare(0 /* opts */)); @@ -118,10 +115,25 @@ int main(int /*argc*/, char** /*argv*/) { } service->sayHiToStatsCompanion(); - // Start the log reader thread - err = start_log_reader_thread(service); - if (err != NO_ERROR) { - return 1; + service->Startup(); + + sp socketListener = new StatsSocketListener(service); + + if (kUseLogd) { + ALOGI("using logd"); + // Start the log reader thread + status_t err = start_log_reader_thread(service); + if (err != NO_ERROR) { + return 1; + } + } + + if (kUseStatsdSocket) { + ALOGI("using statsd socket"); + // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value + if (socketListener->startListener(600)) { + exit(1); + } } // Loop forever -- the reports run on this thread in a handler, and the diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp new file mode 100755 index 0000000000000..0392d6756292d --- /dev/null +++ b/cmds/statsd/src/socket/StatsSocketListener.cpp @@ -0,0 +1,136 @@ +/* + * 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. + */ +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "StatsSocketListener.h" +#include "guardrail/StatsdStats.h" +#include "stats_log_util.h" + +namespace android { +namespace os { +namespace statsd { + +static const int kLogMsgHeaderSize = 28; + +StatsSocketListener::StatsSocketListener(const sp& listener) + : SocketListener(getLogSocket(), false /*start listen*/), mListener(listener) { +} + +StatsSocketListener::~StatsSocketListener() { +} + +bool StatsSocketListener::onDataAvailable(SocketClient* cli) { + static bool name_set; + if (!name_set) { + prctl(PR_SET_NAME, "statsd.writer"); + name_set = true; + } + + // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received + char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD + + 1]; + struct iovec iov = {buffer, sizeof(buffer) - 1}; + + alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))]; + struct msghdr hdr = { + NULL, 0, &iov, 1, control, sizeof(control), 0, + }; + + int socket = cli->getSocket(); + + // To clear the entire buffer is secure/safe, but this contributes to 1.68% + // overhead under logging load. We are safe because we check counts, but + // still need to clear null terminator + // memset(buffer, 0, sizeof(buffer)); + ssize_t n = recvmsg(socket, &hdr, 0); + if (n <= (ssize_t)(sizeof(android_log_header_t))) { + return false; + } + + buffer[n] = 0; + + struct ucred* cred = NULL; + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); + while (cmsg != NULL) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { + cred = (struct ucred*)CMSG_DATA(cmsg); + break; + } + cmsg = CMSG_NXTHDR(&hdr, cmsg); + } + + struct ucred fake_cred; + if (cred == NULL) { + cred = &fake_cred; + cred->pid = 0; + cred->uid = DEFAULT_OVERFLOWUID; + } + + char* ptr = ((char*)buffer) + sizeof(android_log_header_t); + n -= sizeof(android_log_header_t); + + log_msg msg; + + msg.entry.len = n; + msg.entry.hdr_size = kLogMsgHeaderSize; + msg.entry.sec = time(nullptr); + msg.entry.pid = cred->pid; + msg.entry.uid = cred->uid; + + memcpy(msg.buf + kLogMsgHeaderSize, ptr, n + 1); + LogEvent event(msg); + + // Call the listener + mListener->OnLogEvent(&event, false /*reconnected, N/A in statsd socket*/); + + return true; +} + +int StatsSocketListener::getLogSocket() { + static const char socketName[] = "statsdw"; + int sock = android_get_control_socket(socketName); + + if (sock < 0) { // statsd started up in init.sh + sock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM); + + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { + return -1; + } + } + return sock; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h new file mode 100644 index 0000000000000..73e4d33d3e18b --- /dev/null +++ b/cmds/statsd/src/socket/StatsSocketListener.h @@ -0,0 +1,54 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include "logd/LogListener.h" + +// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of +// the uapi headers for userspace to use. This value is filled in on the +// out-of-band socket credentials if the OS fails to find one available. +// One of the causes of this is if SO_PASSCRED is set, all the packets before +// that point will have this value. We also use it in a fake credential if +// no socket credentials are supplied. +#ifndef DEFAULT_OVERFLOWUID +#define DEFAULT_OVERFLOWUID 65534 +#endif + +namespace android { +namespace os { +namespace statsd { + +class StatsSocketListener : public SocketListener, public virtual android::RefBase { +public: + StatsSocketListener(const sp& listener); + + virtual ~StatsSocketListener(); + +protected: + virtual bool onDataAvailable(SocketClient* cli); + +private: + static int getLogSocket(); + /** + * Who is going to get the events when they're read. + */ + sp mListener; +}; +} // namespace statsd +} // namespace os +} // namespace android \ No newline at end of file diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc index 3a0c22425e5cc..f3492920940de 100644 --- a/cmds/statsd/statsd.rc +++ b/cmds/statsd/statsd.rc @@ -14,6 +14,7 @@ service statsd /system/bin/statsd class main + socket statsdw dgram+passcred 0222 statsd statsd user statsd group statsd log writepid /dev/cpuset/system-background/tasks diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 213fa8e93320d..0f04b5d164542 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -146,8 +146,6 @@ cc_defaults { name: "libhwui_defaults", defaults: ["hwui_defaults"], - shared_libs: ["libstatslog"], - whole_static_libs: ["libskia"], srcs: [ @@ -336,7 +334,6 @@ cc_test { ], shared_libs: [ "libmemunreachable", - "libstatslog", ], cflags: [ "-include debug/wrap_gles.h", @@ -404,7 +401,6 @@ cc_benchmark { whole_static_libs: ["libhwui"], shared_libs: [ "libmemunreachable", - "libstatslog", ], srcs: [ @@ -429,7 +425,6 @@ cc_benchmark { whole_static_libs: ["libhwui_static_debug"], shared_libs: [ "libmemunreachable", - "libstatslog", ], srcs: [ diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 81a79801073c3..e6d2a6f555ac2 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include @@ -182,7 +181,6 @@ void JankTracker::finishFrame(const FrameInfo& frame) { ALOGI("%s", ss.str().c_str()); // Just so we have something that counts up, the value is largely irrelevant ATRACE_INT(ss.str().c_str(), ++sDaveyCount); - android::util::stats_write(android::util::DAVEY_OCCURRED, getuid(), ns2ms(totalDuration)); } } diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index 17819dbabcb75..73b715aa5e1a5 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -98,9 +98,16 @@ cc_library_shared { name: "libstatslog", generated_sources: ["statslog.cpp"], generated_headers: ["statslog.h"], + srcs: [ + "stats_event_list.cpp", + "statsd_writer.cpp", + ], cflags: [ "-Wall", "-Werror", + "-DLIBLOG_LOG_TAG=1006", + "-DWRITE_TO_STATSD=1", + "-DWRITE_TO_LOGD=0", ], export_generated_headers: ["statslog.h"], shared_libs: [ diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 4146e02cdd344..638549d395b39 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -104,7 +104,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "#include \n"); fprintf(out, "#include \n"); fprintf(out, "#include \n"); - fprintf(out, "#include \n"); + fprintf(out, "#include \n"); fprintf(out, "#include \n"); fprintf(out, "#include \n"); fprintf(out, "#include \n"); @@ -242,7 +242,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "{\n"); argIndex = 1; - fprintf(out, " android_log_event_list event(kStatsEventTag);\n"); + fprintf(out, " stats_event_list event(kStatsEventTag);\n"); fprintf(out, " event << android::elapsedRealtimeNano();\n\n"); fprintf(out, " event << code;\n\n"); for (vector::const_iterator arg = signature->begin(); @@ -375,7 +375,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "{\n"); argIndex = 1; - fprintf(out, " android_log_event_list event(kStatsEventTag);\n"); + fprintf(out, " stats_event_list event(kStatsEventTag);\n"); fprintf(out, " event << android::elapsedRealtimeNano();\n\n"); fprintf(out, " event << code;\n\n"); for (vector::const_iterator arg = signature->begin(); diff --git a/tools/stats_log_api_gen/stats_event_list.cpp b/tools/stats_log_api_gen/stats_event_list.cpp new file mode 100644 index 0000000000000..d456ef0bac827 --- /dev/null +++ b/tools/stats_log_api_gen/stats_event_list.cpp @@ -0,0 +1,165 @@ +/* + * 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 "stats_event_list.h" + +#include "statsd_writer.h" + +namespace android { +namespace util { + +enum ReadWriteFlag { + kAndroidLoggerRead = 1, + kAndroidLoggerWrite = 2, +}; + +typedef struct { + uint32_t tag; + unsigned pos; /* Read/write position into buffer */ + unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */ + unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */ + unsigned list_nest_depth; + unsigned len; /* Length or raw buffer. */ + bool overflow; + bool list_stop; /* next call decrement list_nest_depth and issue a stop */ + ReadWriteFlag read_write_flag; + uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD]; +} android_log_context_internal; + +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; + +int stats_write_list(android_log_context ctx) { + android_log_context_internal* context; + const char* msg; + ssize_t len; + + context = (android_log_context_internal*)(ctx); + if (!context || (kAndroidLoggerWrite != context->read_write_flag)) { + return -EBADF; + } + + if (context->list_nest_depth) { + return -EIO; + } + + /* NB: if there was overflow, then log is truncated. Nothing reported */ + context->storage[1] = context->count[0]; + len = context->len = context->pos; + msg = (const char*)context->storage; + /* it's not a list */ + if (context->count[0] <= 1) { + len -= sizeof(uint8_t) + sizeof(uint8_t); + if (len < 0) { + len = 0; + } + msg += sizeof(uint8_t) + sizeof(uint8_t); + } + + struct iovec vec[2]; + vec[0].iov_base = &context->tag; + vec[0].iov_len = sizeof(context->tag); + vec[1].iov_base = (void*)msg; + vec[1].iov_len = len; + return write_to_statsd(vec, 2); +} + +int stats_event_list::write_to_logger(android_log_context ctx, log_id_t id) { + int retValue = 0; + + if (WRITE_TO_LOGD) { + retValue = android_log_write_list(ctx, id); + } + + if (WRITE_TO_STATSD) { + // log_event_list's cast operator is overloaded. + int ret = stats_write_list(static_cast(*this)); + // In debugging phase, we may write to both logd and statsd. Prefer to return + // statsd socket write error code here. + if (ret < 0) { + retValue = ret; + } + } + + return retValue; +} + +/* log_init_lock assumed */ +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_stats_daemon(struct iovec* vec, size_t nr) { + int ret, 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; + clock_gettime(CLOCK_REALTIME, &ts); + + ret = 0; + + ssize_t retval; + retval = (*statsdLoggerWrite.write)(&ts, vec, nr); + if (ret >= 0) { + ret = retval; + } + + errno = save_errno; + return ret; +} + +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; +} + +} // namespace util +} // namespace android \ No newline at end of file diff --git a/tools/stats_log_api_gen/stats_event_list.h b/tools/stats_log_api_gen/stats_event_list.h new file mode 100644 index 0000000000000..66b9918726fd9 --- /dev/null +++ b/tools/stats_log_api_gen/stats_event_list.h @@ -0,0 +1,219 @@ +/* + * 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_EVENT_LIST_H +#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H + +#include + +namespace android { +namespace util { + +/** + * A copy of android_log_event_list class. + * + * android_log_event_list is going to be deprecated soon, so copy it here to avoid creating + * dependency on upstream code. TODO(b/78304629): Rewrite this code. + */ +class stats_event_list { +private: + android_log_context ctx; + int ret; + + stats_event_list(const stats_event_list&) = delete; + void operator=(const stats_event_list&) = delete; + + int write_to_logger(android_log_context context, log_id_t id); + +public: + explicit stats_event_list(int tag) : ret(0) { + ctx = create_android_logger(static_cast(tag)); + } + explicit stats_event_list(log_msg& log_msg) : ret(0) { + ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t), + log_msg.entry.len - sizeof(uint32_t)); + } + ~stats_event_list() { + android_log_destroy(&ctx); + } + + int close() { + int retval = android_log_destroy(&ctx); + if (retval < 0) ret = retval; + return retval; + } + + /* To allow above C calls to use this class as parameter */ + operator android_log_context() const { + return ctx; + } + + /* return errors or transmit status */ + int status() const { + return ret; + } + + int begin() { + int retval = android_log_write_list_begin(ctx); + if (retval < 0) ret = retval; + return ret; + } + int end() { + int retval = android_log_write_list_end(ctx); + if (retval < 0) ret = retval; + return ret; + } + + stats_event_list& operator<<(int32_t value) { + int retval = android_log_write_int32(ctx, value); + if (retval < 0) ret = retval; + return *this; + } + + stats_event_list& operator<<(uint32_t value) { + int retval = android_log_write_int32(ctx, static_cast(value)); + if (retval < 0) ret = retval; + return *this; + } + + stats_event_list& operator<<(bool value) { + int retval = android_log_write_int32(ctx, value ? 1 : 0); + if (retval < 0) ret = retval; + return *this; + } + + stats_event_list& operator<<(int64_t value) { + int retval = android_log_write_int64(ctx, value); + if (retval < 0) ret = retval; + return *this; + } + + stats_event_list& operator<<(uint64_t value) { + int retval = android_log_write_int64(ctx, static_cast(value)); + if (retval < 0) ret = retval; + return *this; + } + + stats_event_list& operator<<(const char* value) { + int retval = android_log_write_string8(ctx, value); + if (retval < 0) ret = retval; + return *this; + } + +#if defined(_USING_LIBCXX) + stats_event_list& operator<<(const std::string& value) { + int retval = android_log_write_string8_len(ctx, value.data(), value.length()); + if (retval < 0) ret = retval; + return *this; + } +#endif + + stats_event_list& operator<<(float value) { + int retval = android_log_write_float32(ctx, value); + if (retval < 0) ret = retval; + return *this; + } + + int write(log_id_t id = LOG_ID_EVENTS) { + /* facilitate -EBUSY retry */ + if ((ret == -EBUSY) || (ret > 0)) ret = 0; + int retval = write_to_logger(ctx, id); + /* existing errors trump transmission errors */ + if (!ret) ret = retval; + return ret; + } + + int operator<<(log_id_t id) { + write(id); + android_log_destroy(&ctx); + return ret; + } + + /* + * Append methods removes any integer promotion + * confusion, and adds access to string with length. + * Append methods are also added for all types for + * convenience. + */ + + bool AppendInt(int32_t value) { + int retval = android_log_write_int32(ctx, value); + if (retval < 0) ret = retval; + return ret >= 0; + } + + bool AppendLong(int64_t value) { + int retval = android_log_write_int64(ctx, value); + if (retval < 0) ret = retval; + return ret >= 0; + } + + bool AppendString(const char* value) { + int retval = android_log_write_string8(ctx, value); + if (retval < 0) ret = retval; + return ret >= 0; + } + + bool AppendString(const char* value, size_t len) { + int retval = android_log_write_string8_len(ctx, value, len); + if (retval < 0) ret = retval; + return ret >= 0; + } + +#if defined(_USING_LIBCXX) + bool AppendString(const std::string& value) { + int retval = android_log_write_string8_len(ctx, value.data(), value.length()); + if (retval < 0) ret = retval; + return ret; + } + + bool Append(const std::string& value) { + int retval = android_log_write_string8_len(ctx, value.data(), value.length()); + if (retval < 0) ret = retval; + return ret; + } +#endif + + bool AppendFloat(float value) { + int retval = android_log_write_float32(ctx, value); + if (retval < 0) ret = retval; + return ret >= 0; + } + + template + bool Append(Tvalue value) { + *this << value; + return ret >= 0; + } + + bool Append(const char* value, size_t len) { + int retval = android_log_write_string8_len(ctx, value, len); + if (retval < 0) ret = retval; + return ret >= 0; + } + + android_log_list_element read() { + return android_log_read_next(ctx); + } + android_log_list_element peek() { + return android_log_peek_next(ctx); + } +}; + +} // namespace util +} // namespace android + +#endif // ANDROID_STATS_LOG_STATS_EVENT_LIST_H diff --git a/tools/stats_log_api_gen/statsd_writer.cpp b/tools/stats_log_api_gen/statsd_writer.cpp new file mode 100644 index 0000000000000..d736f7e217a9a --- /dev/null +++ b/tools/stats_log_api_gen/statsd_writer.cpp @@ -0,0 +1,272 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* branchless on many architectures. */ +#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) + +namespace android { +namespace util { + +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; + +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); + +struct android_log_transport_write statsdLoggerWrite = { + .name = "statsd", + .available = statsdAvailable, + .open = statsdOpen, + .close = statsdClose, + .write = statsdWrite, +}; + +std::atomic_int android_log_transport_write::sock(-EBADF); + +/* log_init_lock assumed */ +static int statsdOpen() { + int i, ret = 0; + + i = atomic_load(&statsdLoggerWrite.sock); + if (i < 0) { + int sock = TEMP_FAILURE_RETRY( + socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)); + if (sock < 0) { + ret = -errno; + } else { + 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 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; + static atomic_int dropped; + + 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_int_t buffer; + header.id = LOG_ID_STATS; + buffer.header.tag = htole32(LIBLOG_LOG_TAG); + buffer.payload.type = EVENT_TYPE_INT; + buffer.payload.data = htole32(snapshot); + + 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); + } else if (ret == -EAGAIN) { + atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed); + } + + return ret; +} + +} // namespace util +} // namespace android \ No newline at end of file diff --git a/tools/stats_log_api_gen/statsd_writer.h b/tools/stats_log_api_gen/statsd_writer.h new file mode 100644 index 0000000000000..05ebc6cbe0ead --- /dev/null +++ b/tools/stats_log_api_gen/statsd_writer.h @@ -0,0 +1,49 @@ +/* + * 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 +#include +#include + +namespace android { +namespace util { + +/** + * 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 */ + static std::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); +}; + +} // namespace util +} // namespace android + +#endif // ANDROID_STATS_LOG_STATS_WRITER_H