1 /* 2 * Copyright (C) 2014 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 package android.support.test.aupt; 18 19 import android.app.Instrumentation; 20 import android.os.Environment; 21 import android.os.ParcelFileDescriptor; 22 import android.os.SystemClock; 23 import android.util.Log; 24 25 import java.io.ByteArrayOutputStream; 26 import java.io.File; 27 import java.io.FileNotFoundException; 28 import java.io.FileOutputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.text.SimpleDateFormat; 33 import java.util.Date; 34 import java.util.Locale; 35 36 public class DataCollector { 37 private static final String TAG = "AuptDataCollector"; 38 private long mBugreportInterval, mMeminfoInterval, mCpuinfoInterval, mFragmentationInterval, 39 mIonHeapInterval, mPageTypeInfoInterval, mTraceInterval; 40 private File mResultsDirectory; 41 42 private Thread mLoggerThread; 43 private Logger mLogger; 44 private Instrumentation mInstrumentation; 45 DataCollector(long bugreportInterval, long meminfoInterval, long cpuinfoInterval, long fragmentationInterval, long ionHeapInterval, long pagetypeinfoInterval, long traceInterval, File outputLocation, Instrumentation intrumentation)46 public DataCollector(long bugreportInterval, long meminfoInterval, long cpuinfoInterval, 47 long fragmentationInterval, long ionHeapInterval, long pagetypeinfoInterval, 48 long traceInterval, File outputLocation, Instrumentation intrumentation) { 49 mBugreportInterval = bugreportInterval; 50 mMeminfoInterval = meminfoInterval; 51 mCpuinfoInterval = cpuinfoInterval; 52 mFragmentationInterval = fragmentationInterval; 53 mIonHeapInterval = ionHeapInterval; 54 mPageTypeInfoInterval = pagetypeinfoInterval; 55 mResultsDirectory = outputLocation; 56 mTraceInterval = traceInterval; 57 mInstrumentation = intrumentation; 58 } 59 start()60 public void start() { 61 mLogger = new Logger(); 62 mLoggerThread = new Thread(mLogger); 63 mLoggerThread.start(); 64 } 65 stop()66 public void stop() { 67 mLogger.stop(); 68 try { 69 mLoggerThread.join(); 70 } catch (InterruptedException e) { 71 // ignore 72 } 73 } 74 75 private class Logger implements Runnable { 76 private final long mIntervals[] = { 77 mBugreportInterval, mMeminfoInterval, mCpuinfoInterval, mFragmentationInterval, 78 mIonHeapInterval, mPageTypeInfoInterval, mTraceInterval 79 }; 80 private final LogGenerator mLoggers[] = { 81 new BugreportGenerator(), new CompactMemInfoGenerator(), new CpuInfoGenerator(), 82 new FragmentationGenerator(), new IonHeapGenerator(), new PageTypeInfoGenerator(), 83 new TraceGenerator() 84 }; 85 86 private final long mLastUpdate[] = new long[mLoggers.length]; 87 private final long mSleepInterval; 88 89 private boolean mStopped = false; 90 Logger()91 public Logger() { 92 for (int i = 0; i < mIntervals.length; i++) { 93 if (mIntervals[i] > 0) { 94 try { 95 mLoggers[i].createLog(); 96 } catch (InterruptedException e) { 97 // ignore 98 } 99 mLastUpdate[i] = SystemClock.uptimeMillis(); 100 } 101 } 102 103 mSleepInterval = gcd(mIntervals); 104 } 105 stop()106 public void stop() { 107 synchronized(this) { 108 mStopped = true; 109 notifyAll(); 110 } 111 } 112 gcd(long values[])113 private long gcd(long values[]) { 114 if (values.length < 2) 115 return 0; 116 117 long gcdSoFar = values[0]; 118 for (int i = 1; i < values.length; i++) { 119 gcdSoFar = gcd(gcdSoFar, values[i]); 120 } 121 122 return gcdSoFar; 123 } 124 gcd(long a, long b)125 private long gcd(long a, long b) { 126 if (a == 0) 127 return b; 128 if (b == 0) 129 return a; 130 if (a > b) 131 return gcd(b, a % b); 132 else 133 return gcd(a, b % a); 134 } 135 136 @Override run()137 public void run() { 138 if (mSleepInterval <= 0) 139 return; 140 141 synchronized(this) { 142 while (!mStopped) { 143 try { 144 for (int i = 0; i < mIntervals.length; i++) { 145 if (mIntervals[i] > 0 && 146 SystemClock.uptimeMillis() - mLastUpdate[i] > mIntervals[i]) { 147 mLoggers[i].createLog(); 148 mLastUpdate[i] = SystemClock.uptimeMillis(); 149 } 150 } 151 wait(mSleepInterval); 152 } catch (InterruptedException e) { 153 // Ignore. 154 } 155 } 156 } 157 } 158 } 159 160 private interface LogGenerator { createLog()161 public void createLog() throws InterruptedException; 162 } 163 164 private class CompactMemInfoGenerator implements LogGenerator { 165 @Override createLog()166 public void createLog() throws InterruptedException { 167 try { 168 saveCompactMeminfo(mResultsDirectory + "/compact-meminfo-%s.txt"); 169 } catch (IOException ioe) { 170 Log.w(TAG, "Error while saving dumpsys meminfo -c: " + ioe.getMessage()); 171 } 172 } 173 } 174 175 private class CpuInfoGenerator implements LogGenerator { 176 @Override createLog()177 public void createLog() throws InterruptedException { 178 try { 179 saveCpuinfo(mResultsDirectory + "/cpuinfo-%s.txt"); 180 } catch (IOException ioe) { 181 Log.w(TAG, "Error while saving dumpsys cpuinfo : " + ioe.getMessage()); 182 } 183 } 184 } 185 186 private class BugreportGenerator implements LogGenerator { 187 @Override createLog()188 public void createLog() throws InterruptedException { 189 try { 190 saveBugreport(mResultsDirectory + "/bugreport-%s.txt"); 191 } catch (IOException e) { 192 Log.w(TAG, String.format("Failed to take bugreport: %s", e.getMessage())); 193 } 194 } 195 } 196 197 private class FragmentationGenerator implements LogGenerator { 198 @Override createLog()199 public void createLog() throws InterruptedException { 200 try { 201 saveFragmentation(mResultsDirectory + "/unusable-index-%s.txt"); 202 } catch (IOException e) { 203 Log.w(TAG, String.format("Failed to save buddyinfo: %s", e.getMessage())); 204 } 205 } 206 } 207 208 private class IonHeapGenerator implements LogGenerator { 209 @Override createLog()210 public void createLog() throws InterruptedException { 211 try { 212 saveIonHeap("audio", mResultsDirectory + "/ion-audio-%s.txt"); 213 saveIonHeap("system", mResultsDirectory + "/ion-system-%s.txt"); 214 } catch (IOException e) { 215 Log.w(TAG, String.format("Failed to save ION heap: %s", e.getMessage())); 216 } 217 } 218 } 219 220 private class PageTypeInfoGenerator implements LogGenerator { 221 @Override createLog()222 public void createLog() throws InterruptedException { 223 try { 224 savePageTypeInfo(mResultsDirectory + "/pagetypeinfo-%s.txt"); 225 } catch (IOException e) { 226 Log.w(TAG, String.format("Failed to save pagetypeinfo: %s", e.getMessage())); 227 } 228 } 229 } 230 231 private class TraceGenerator implements LogGenerator { 232 @Override createLog()233 public void createLog() throws InterruptedException { 234 try { 235 saveTrace(mResultsDirectory + "/trace-%s.txt"); 236 } catch (IOException e) { 237 Log.w(TAG, String.format("Failed to save trace: %s", e.getMessage())); 238 } 239 } 240 } 241 saveCompactMeminfo(String filename)242 public void saveCompactMeminfo(String filename) 243 throws FileNotFoundException, IOException, InterruptedException { 244 saveProcessOutput("dumpsys meminfo -c -S", filename); 245 } 246 saveCpuinfo(String filename)247 public void saveCpuinfo(String filename) 248 throws FileNotFoundException, IOException, InterruptedException { 249 saveProcessOutput("dumpsys cpuinfo", filename); 250 } 251 saveFragmentation(String filename)252 public void saveFragmentation(String filename) 253 throws FileNotFoundException, IOException, InterruptedException { 254 saveProcessOutput("cat /d/extfrag/unusable_index", filename); 255 } 256 saveIonHeap(String type, String filename)257 public void saveIonHeap(String type, String filename) 258 throws FileNotFoundException, IOException, InterruptedException { 259 saveProcessOutput(String.format("cat /d/ion/heaps/%s", type), filename); 260 } 261 savePageTypeInfo(String filename)262 public void savePageTypeInfo(String filename) 263 throws FileNotFoundException, IOException, InterruptedException { 264 saveProcessOutput("cat /proc/pagetypeinfo", filename); 265 } 266 saveTrace(String filename)267 public void saveTrace(String filename) 268 throws FileNotFoundException, IOException, InterruptedException { 269 saveProcessOutput("cat /sys/kernel/debug/tracing/trace", filename); 270 } 271 saveBugreport(String filename)272 public void saveBugreport(String filename) 273 throws IOException, InterruptedException { 274 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 275 // Spaces matter in the following command line. Make sure there are no spaces 276 // in the filename and around the '>' sign. 277 String cmdline = String.format("/system/bin/sh -c /system/bin/bugreport>%s", 278 templateToFilename(filename)); 279 saveProcessOutput(cmdline, baos); 280 baos.close(); 281 } 282 dumpMeminfo(String notes)283 public void dumpMeminfo(String notes) { 284 long epochSeconds = System.currentTimeMillis() / 1000; 285 File outputDir = new File(Environment.getExternalStorageDirectory(), "meminfo"); 286 Log.i(TAG, outputDir.toString()); 287 if (!outputDir.exists()) { 288 boolean yes = outputDir.mkdirs(); 289 Log.i(TAG, yes ? "created" : "not created"); 290 } 291 File outputFile = new File(outputDir, String.format("%d.txt", epochSeconds)); 292 Log.i(TAG, outputFile.toString()); 293 FileOutputStream fos = null; 294 295 try { 296 fos = new FileOutputStream(outputFile); 297 fos.write(String.format("notes: %s\n\n", notes).getBytes()); 298 299 saveProcessOutput("dumpsys meminfo -c", fos); 300 fos.close(); 301 } catch (FileNotFoundException e) { 302 Log.e(TAG, "exception while dumping meminfo", e); 303 } catch (IOException e) { 304 Log.e(TAG, "exception while dumping meminfo", e); 305 } 306 } 307 saveProcessOutput(String command, String filenameTemplate)308 private void saveProcessOutput(String command, String filenameTemplate) 309 throws IOException, FileNotFoundException { 310 String outFilename = templateToFilename(filenameTemplate); 311 File file = new File(outFilename); 312 Log.d(TAG, String.format("Saving command \"%s\" output into file %s", 313 command, file.getAbsolutePath())); 314 315 OutputStream out = new FileOutputStream(file); 316 saveProcessOutput(command, out); 317 out.close(); 318 } 319 templateToFilename(String filenameTemplate)320 private String templateToFilename(String filenameTemplate) { 321 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); 322 return String.format(filenameTemplate, sdf.format(new Date())); 323 } 324 saveProcessOutput(String command, OutputStream out)325 public void saveProcessOutput(String command, OutputStream out) throws IOException { 326 InputStream in = null; 327 try { 328 ParcelFileDescriptor pfd = 329 mInstrumentation.getUiAutomation().executeShellCommand(command); 330 in = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 331 byte[] buffer = new byte[4096]; //4K buffer 332 int bytesRead = -1; 333 while (true) { 334 bytesRead = in.read(buffer); 335 if (bytesRead == -1) { 336 break; 337 } 338 out.write(buffer, 0, bytesRead); 339 } 340 } finally { 341 if (in != null) { 342 in.close(); 343 } 344 if (out != null) { 345 out.flush(); 346 } 347 } 348 } 349 } 350