• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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