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 25using namespace std; 26 27const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds 28 29// ================================================================================ 30Section::Section(int i) 31 :id(i) 32{ 33} 34 35Section::~Section() 36{ 37} 38 39status_t 40Section::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// ================================================================================ 49struct 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 62 int readFd() { return fds[0]; } 63 int writeFd() { return fds[1]; } 64}; 65 66WorkerThreadData::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 75WorkerThreadData::~WorkerThreadData() 76{ 77} 78 79// ================================================================================ 80WorkerThreadSection::WorkerThreadSection(int id) 81 :Section(id) 82{ 83} 84 85WorkerThreadSection::~WorkerThreadSection() 86{ 87} 88 89static void* 90worker_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 107status_t 108WorkerThreadSection::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// ================================================================================ 207CommandSection::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 236CommandSection::~CommandSection() 237{ 238} 239 240status_t 241CommandSection::Execute(ReportRequestSet* /*requests*/) const 242{ 243 return NO_ERROR; 244} 245 246// ================================================================================ 247DumpsysSection::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 268DumpsysSection::~DumpsysSection() 269{ 270} 271 272status_t 273DumpsysSection::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