1 /*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #define STATSD_DEBUG false // STOPSHIP if true
17 #include "Log.h"
18
19 #include "BaseStatsSocketListener.h"
20
21 #include <ctype.h>
22 #include <cutils/sockets.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <sys/cdefs.h>
26 #include <sys/prctl.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <sys/un.h>
30 #include <thread>
31 #include <unistd.h>
32
33 #include "guardrail/StatsdStats.h"
34 #include "android-base/scopeguard.h"
35 #include "logd/logevent_util.h"
36 #include "stats_log_util.h"
37 #include "statslog_statsd.h"
38 #include "utils/api_tracing.h"
39
40 using namespace std;
41
42 namespace android {
43 namespace os {
44 namespace statsd {
BaseStatsSocketListener(const std::shared_ptr<LogEventQueue> & queue,const std::shared_ptr<LogEventFilter> & logEventFilter)45 BaseStatsSocketListener::BaseStatsSocketListener(const std::shared_ptr<LogEventQueue>& queue,
46 const std::shared_ptr<LogEventFilter>& logEventFilter)
47 : mQueue(queue),
48 mLogEventFilter(logEventFilter){};
49
processSocketMessage(void * buffer,const uint32_t len,uint32_t uid,uint32_t pid)50 tuple<int32_t, int64_t> BaseStatsSocketListener::processSocketMessage(void* buffer,
51 const uint32_t len, uint32_t uid,
52 uint32_t pid) {
53 ATRACE_CALL_DEBUG();
54 static const uint32_t kStatsEventTag = 1937006964;
55
56 if (len <= (ssize_t)(sizeof(android_log_header_t)) + sizeof(uint32_t)) {
57 return {-1, 0};
58 }
59
60 const uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t);
61 uint32_t bufferLen = len - sizeof(android_log_header_t);
62
63 // When a log failed to write to statsd socket (e.g., due ot EBUSY), a special message would
64 // be sent to statsd when the socket communication becomes available again.
65 // The format is android_log_event_int_t with a single integer in the payload indicating the
66 // number of logs that failed. (*FORMAT MUST BE IN SYNC WITH system/core/libstats*)
67 // Note that all normal stats logs are in the format of event_list, so there won't be confusion.
68 //
69 // TODO(b/80538532): In addition to log it in StatsdStats, we should properly reset the config.
70 if (bufferLen == sizeof(android_log_event_long_t)) {
71 const android_log_event_long_t* long_event =
72 reinterpret_cast<const android_log_event_long_t*>(ptr);
73 if (long_event->payload.type == EVENT_TYPE_LONG) {
74 int64_t composed_long = long_event->payload.data;
75
76 // format:
77 // |last_tag|dropped_count|
78 int32_t dropped_count = (int32_t)(0xffffffff & composed_long);
79 int32_t last_atom_tag = (int32_t)((0xffffffff00000000 & (uint64_t)composed_long) >> 32);
80
81 ALOGE("Found dropped events: %d error %d last atom tag %d from uid %d", dropped_count,
82 long_event->header.tag, last_atom_tag, uid);
83 StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(), dropped_count,
84 long_event->header.tag, last_atom_tag, uid, pid);
85 return {-1, 0};
86 }
87 }
88
89 // test that received valid StatsEvent buffer
90 const uint32_t statsEventTag = *reinterpret_cast<const uint32_t*>(ptr);
91 if (statsEventTag != kStatsEventTag) {
92 return {-1, 0};
93 }
94
95 // move past the 4-byte StatsEventTag
96 const uint8_t* msg = ptr + sizeof(uint32_t);
97 bufferLen -= sizeof(uint32_t);
98
99 return processStatsEventBuffer(msg, bufferLen, uid, pid, *mQueue, *mLogEventFilter);
100 }
101
processStatsEventBuffer(const uint8_t * msg,const uint32_t len,uint32_t uid,uint32_t pid,LogEventQueue & queue,const LogEventFilter & filter)102 tuple<int32_t, int64_t> BaseStatsSocketListener::processStatsEventBuffer(const uint8_t* msg,
103 const uint32_t len,
104 uint32_t uid, uint32_t pid,
105 LogEventQueue& queue,
106 const LogEventFilter& filter) {
107 ATRACE_CALL_DEBUG();
108 std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
109
110 if (filter.getFilteringEnabled()) {
111 const LogEvent::BodyBufferInfo bodyInfo = logEvent->parseHeader(msg, len);
112 if (filter.isAtomInUse(logEvent->GetTagId())) {
113 logEvent->parseBody(bodyInfo);
114 }
115 } else {
116 logEvent->parseBuffer(msg, len);
117 }
118
119 const int32_t atomId = logEvent->GetTagId();
120 const bool isAtomSkipped = logEvent->isParsedHeaderOnly();
121 const int64_t atomTimestamp = logEvent->GetElapsedTimestampNs();
122
123 // Tell StatsdStats about new event
124 StatsdStats::getInstance().noteAtomLogged(atomId, atomTimestamp, isAtomSkipped);
125
126 if (atomId == util::STATS_SOCKET_LOSS_REPORTED) {
127 if (isAtomSkipped) {
128 ALOGW("Atom STATS_SOCKET_LOSS_REPORTED should not be skipped");
129 }
130
131 // handling socket loss info reported atom
132 // processing it here to not lose info due to queue overflow
133 const std::optional<SocketLossInfo>& lossInfo = toSocketLossInfo(*logEvent);
134 if (lossInfo) {
135 StatsdStats::getInstance().noteAtomSocketLoss(*lossInfo);
136 } else {
137 ALOGW("Atom STATS_SOCKET_LOSS_REPORTED content is invalid");
138 }
139 }
140
141 const auto [success, oldestTimestamp, queueSize] = queue.push(std::move(logEvent));
142 if (success) {
143 StatsdStats::getInstance().noteEventQueueSize(queueSize, atomTimestamp);
144 } else {
145 StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp, atomId);
146 }
147 return {atomId, atomTimestamp};
148 }
149
getLogSocket()150 int BaseStatsSocketListener::getLogSocket() {
151 static const char socketName[] = "statsdw";
152 int sock = android_get_control_socket(socketName);
153
154 if (sock < 0) { // statsd started up in init.sh
155 sock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
156
157 int on = 1;
158 if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
159 return -1;
160 }
161 }
162 return sock;
163 }
164
noteBatchSocketRead(int32_t size,int64_t lastReadTimeNs,int64_t currReadTimeNs,int64_t minAtomReadTimeNs,int64_t maxAtomReadTimeNs,const std::unordered_map<int32_t,int32_t> & atomCounts)165 void BaseStatsSocketListener::noteBatchSocketRead(int32_t size, int64_t lastReadTimeNs,
166 int64_t currReadTimeNs, int64_t minAtomReadTimeNs,
167 int64_t maxAtomReadTimeNs,
168 const std::unordered_map <int32_t, int32_t> &atomCounts) {
169 StatsdStats::getInstance().noteBatchSocketRead(size, lastReadTimeNs, currReadTimeNs,
170 minAtomReadTimeNs, maxAtomReadTimeNs, atomCounts);
171 mLastSocketReadTimeNs = currReadTimeNs;
172 mAtomCounts.clear();
173 }
174
175 } // namespace statsd
176 } // namespace os
177 } // namespace android
178