• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 #define LOG_TAG "incidentd"
18 
19 #include "Section.h"
20 #include "protobuf.h"
21 
22 #include <binder/IServiceManager.h>
23 #include <mutex>
24 
25 using namespace std;
26 
27 const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
28 
29 // ================================================================================
Section(int i)30 Section::Section(int i)
31     :id(i)
32 {
33 }
34 
~Section()35 Section::~Section()
36 {
37 }
38 
39 status_t
WriteHeader(ReportRequestSet * requests,size_t size) const40 Section::WriteHeader(ReportRequestSet* requests, size_t size) const
41 {
42     ssize_t amt;
43     uint8_t buf[20];
44     uint8_t* p = write_length_delimited_tag_header(buf, this->id, size);
45     return requests->write(buf, p-buf);
46 }
47 
48 // ================================================================================
49 struct WorkerThreadData : public virtual RefBase
50 {
51     const WorkerThreadSection* section;
52     int fds[2];
53 
54     // Lock protects these fields
55     mutex lock;
56     bool workerDone;
57     status_t workerError;
58 
59     WorkerThreadData(const WorkerThreadSection* section);
60     virtual ~WorkerThreadData();
61 
readFdWorkerThreadData62     int readFd() { return fds[0]; }
writeFdWorkerThreadData63     int writeFd() { return fds[1]; }
64 };
65 
WorkerThreadData(const WorkerThreadSection * sec)66 WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec)
67     :section(sec),
68      workerDone(false),
69      workerError(NO_ERROR)
70 {
71     fds[0] = -1;
72     fds[1] = -1;
73 }
74 
~WorkerThreadData()75 WorkerThreadData::~WorkerThreadData()
76 {
77 }
78 
79 // ================================================================================
WorkerThreadSection(int id)80 WorkerThreadSection::WorkerThreadSection(int id)
81     :Section(id)
82 {
83 }
84 
~WorkerThreadSection()85 WorkerThreadSection::~WorkerThreadSection()
86 {
87 }
88 
89 static void*
worker_thread_func(void * cookie)90 worker_thread_func(void* cookie)
91 {
92     WorkerThreadData* data = (WorkerThreadData*)cookie;
93     status_t err = data->section->BlockingCall(data->writeFd());
94 
95     {
96         unique_lock<mutex> lock(data->lock);
97         data->workerDone = true;
98         data->workerError = err;
99     }
100 
101     close(data->writeFd());
102     data->decStrong(data->section);
103     // data might be gone now. don't use it after this point in this thread.
104     return NULL;
105 }
106 
107 status_t
Execute(ReportRequestSet * requests) const108 WorkerThreadSection::Execute(ReportRequestSet* requests) const
109 {
110     status_t err = NO_ERROR;
111     pthread_t thread;
112     pthread_attr_t attr;
113     bool timedOut = false;
114     FdBuffer buffer;
115 
116     // Data shared between this thread and the worker thread.
117     sp<WorkerThreadData> data = new WorkerThreadData(this);
118 
119     // Create the pipe
120     err = pipe(data->fds);
121     if (err != 0) {
122         return -errno;
123     }
124 
125     // The worker thread needs a reference and we can't let the count go to zero
126     // if that thread is slow to start.
127     data->incStrong(this);
128 
129     // Create the thread
130     err = pthread_attr_init(&attr);
131     if (err != 0) {
132         return -err;
133     }
134     // TODO: Do we need to tweak thread priority?
135     err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
136     if (err != 0) {
137         pthread_attr_destroy(&attr);
138         return -err;
139     }
140     err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
141     if (err != 0) {
142         pthread_attr_destroy(&attr);
143         return -err;
144     }
145     pthread_attr_destroy(&attr);
146 
147     // Loop reading until either the timeout or the worker side is done (i.e. eof).
148     err = buffer.read(data->readFd(), REMOTE_CALL_TIMEOUT_MS);
149     if (err != NO_ERROR) {
150         // TODO: Log this error into the incident report.
151         ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
152                 strerror(-err));
153     }
154 
155     // Done with the read fd. The worker thread closes the write one so
156     // we never race and get here first.
157     close(data->readFd());
158 
159     // If the worker side is finished, then return its error (which may overwrite
160     // our possible error -- but it's more interesting anyway).  If not, then we timed out.
161     {
162         unique_lock<mutex> lock(data->lock);
163         if (!data->workerDone) {
164             // We timed out
165             timedOut = true;
166         } else {
167             if (data->workerError != NO_ERROR) {
168                 err = data->workerError;
169                 // TODO: Log this error into the incident report.
170                 ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(),
171                         strerror(-err));
172             }
173         }
174     }
175 
176     if (timedOut || buffer.timedOut()) {
177         ALOGW("WorkerThreadSection '%s' timed out", this->name.string());
178         return NO_ERROR;
179     }
180 
181     if (buffer.truncated()) {
182         // TODO: Log this into the incident report.
183     }
184 
185     // TODO: There was an error with the command or buffering. Report that.  For now
186     // just exit with a log messasge.
187     if (err != NO_ERROR) {
188         ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(),
189                 strerror(-err));
190         return NO_ERROR;
191     }
192 
193     // Write the data that was collected
194     ALOGD("section '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
195             (int)buffer.durationMs());
196     WriteHeader(requests, buffer.size());
197     err = buffer.write(requests);
198     if (err != NO_ERROR) {
199         ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
200         return err;
201     }
202 
203     return NO_ERROR;
204 }
205 
206 // ================================================================================
CommandSection(int id,const char * first,...)207 CommandSection::CommandSection(int id, const char* first, ...)
208     :Section(id)
209 {
210     va_list args;
211     int count = 0;
212 
213     va_start(args, first);
214     while (va_arg(args, const char*) != NULL) {
215         count++;
216     }
217     va_end(args);
218 
219     mCommand = (const char**)malloc(sizeof(const char*) * count);
220 
221     mCommand[0] = first;
222     name = first;
223     name += " ";
224     va_start(args, first);
225     for (int i=0; i<count; i++) {
226         const char* arg = va_arg(args, const char*);
227         mCommand[i+1] = arg;
228         if (arg != NULL) {
229             name += va_arg(args, const char*);
230             name += " ";
231         }
232     }
233     va_end(args);
234 }
235 
~CommandSection()236 CommandSection::~CommandSection()
237 {
238 }
239 
240 status_t
Execute(ReportRequestSet *) const241 CommandSection::Execute(ReportRequestSet* /*requests*/) const
242 {
243     return NO_ERROR;
244 }
245 
246 // ================================================================================
DumpsysSection(int id,const char * service,...)247 DumpsysSection::DumpsysSection(int id, const char* service, ...)
248     :WorkerThreadSection(id),
249      mService(service)
250 {
251     name = "dumpsys ";
252     name += service;
253 
254     va_list args;
255     va_start(args, service);
256     while (true) {
257         const char* arg = va_arg(args, const char*);
258         if (arg == NULL) {
259             break;
260         }
261         mArgs.add(String16(arg));
262         name += " ";
263         name += arg;
264     }
265     va_end(args);
266 }
267 
~DumpsysSection()268 DumpsysSection::~DumpsysSection()
269 {
270 }
271 
272 status_t
BlockingCall(int pipeWriteFd) const273 DumpsysSection::BlockingCall(int pipeWriteFd) const
274 {
275     // checkService won't wait for the service to show up like getService will.
276     sp<IBinder> service = defaultServiceManager()->checkService(mService);
277 
278     if (service == NULL) {
279         // Returning an error interrupts the entire incident report, so just
280         // log the failure.
281         // TODO: have a meta record inside the report that would log this
282         // failure inside the report, because the fact that we can't find
283         // the service is good data in and of itself. This is running in
284         // another thread so lock that carefully...
285         ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
286         return NO_ERROR;
287     }
288 
289     service->dump(pipeWriteFd, mArgs);
290 
291     return NO_ERROR;
292 }
293