1 /* 2 * Copyright (C) 2013 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 com.android.launcher3.testing; 18 19 import android.app.Activity; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.content.pm.PackageManager; 25 import android.net.Uri; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.os.Environment; 29 import android.os.IBinder; 30 import android.util.Log; 31 32 import java.io.BufferedInputStream; 33 import java.io.BufferedOutputStream; 34 import java.io.File; 35 import java.io.FileInputStream; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.OutputStream; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.zip.ZipEntry; 43 import java.util.zip.ZipOutputStream; 44 45 public class MemoryDumpActivity extends Activity { 46 private static final String TAG = "MemoryDumpActivity"; 47 48 @Override onCreate(Bundle savedInstanceState)49 public void onCreate(Bundle savedInstanceState) { 50 super.onCreate(savedInstanceState); 51 } 52 zipUp(ArrayList<String> paths)53 public static String zipUp(ArrayList<String> paths) { 54 final int BUFSIZ = 256 * 1024; // 256K 55 final byte[] buf = new byte[BUFSIZ]; 56 final String zipfilePath = String.format("%s/hprof-%d.zip", 57 Environment.getExternalStorageDirectory(), 58 System.currentTimeMillis()); 59 ZipOutputStream zos = null; 60 try { 61 OutputStream os = new FileOutputStream(zipfilePath); 62 zos = new ZipOutputStream(new BufferedOutputStream(os)); 63 for (String filename : paths) { 64 InputStream is = null; 65 try { 66 is = new BufferedInputStream(new FileInputStream(filename)); 67 ZipEntry entry = new ZipEntry(filename); 68 zos.putNextEntry(entry); 69 int len; 70 while ( 0 < (len = is.read(buf, 0, BUFSIZ)) ) { 71 zos.write(buf, 0, len); 72 } 73 zos.closeEntry(); 74 } finally { 75 is.close(); 76 } 77 } 78 } catch (IOException e) { 79 Log.e(TAG, "error zipping up profile data", e); 80 return null; 81 } finally { 82 if (zos != null) { 83 try { 84 zos.close(); 85 } catch (IOException e) { 86 // ugh, whatever 87 } 88 } 89 } 90 return zipfilePath; 91 } 92 dumpHprofAndShare(final Context context, MemoryTracker tracker)93 public static void dumpHprofAndShare(final Context context, MemoryTracker tracker) { 94 final StringBuilder body = new StringBuilder(); 95 96 final ArrayList<String> paths = new ArrayList<String>(); 97 final int myPid = android.os.Process.myPid(); 98 99 final int[] pids_orig = tracker.getTrackedProcesses(); 100 final int[] pids_copy = Arrays.copyOf(pids_orig, pids_orig.length); 101 for (int pid : pids_copy) { 102 MemoryTracker.ProcessMemInfo info = tracker.getMemInfo(pid); 103 if (info != null) { 104 body.append("pid ").append(pid).append(":") 105 .append(" up=").append(info.getUptime()) 106 .append(" pss=").append(info.currentPss) 107 .append(" uss=").append(info.currentUss) 108 .append("\n"); 109 } 110 if (pid == myPid) { 111 final String path = String.format("%s/launcher-memory-%d.ahprof", 112 Environment.getExternalStorageDirectory(), 113 pid); 114 Log.v(TAG, "Dumping memory info for process " + pid + " to " + path); 115 try { 116 android.os.Debug.dumpHprofData(path); // will block 117 } catch (IOException e) { 118 Log.e(TAG, "error dumping memory:", e); 119 } 120 paths.add(path); 121 } 122 } 123 124 String zipfile = zipUp(paths); 125 126 if (zipfile == null) return; 127 128 Intent shareIntent = new Intent(Intent.ACTION_SEND); 129 shareIntent.setType("application/zip"); 130 131 final PackageManager pm = context.getPackageManager(); 132 shareIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("Launcher memory dump (%d)", myPid)); 133 String appVersion; 134 try { 135 appVersion = pm.getPackageInfo(context.getPackageName(), 0).versionName; 136 } catch (PackageManager.NameNotFoundException e) { 137 appVersion = "?"; 138 } 139 140 body.append("\nApp version: ").append(appVersion).append("\nBuild: ").append(Build.DISPLAY).append("\n"); 141 shareIntent.putExtra(Intent.EXTRA_TEXT, body.toString()); 142 143 final File pathFile = new File(zipfile); 144 final Uri pathUri = Uri.fromFile(pathFile); 145 146 shareIntent.putExtra(Intent.EXTRA_STREAM, pathUri); 147 context.startActivity(shareIntent); 148 } 149 150 @Override onStart()151 public void onStart() { 152 super.onStart(); 153 154 startDump(this, new Runnable() { 155 @Override 156 public void run() { 157 finish(); 158 } 159 }); 160 } 161 startDump(final Context context)162 public static void startDump(final Context context) { 163 startDump(context, null); 164 } 165 startDump(final Context context, final Runnable andThen)166 public static void startDump(final Context context, final Runnable andThen) { 167 final ServiceConnection connection = new ServiceConnection() { 168 public void onServiceConnected(ComponentName className, IBinder service) { 169 Log.v(TAG, "service connected, dumping..."); 170 dumpHprofAndShare(context, 171 ((MemoryTracker.MemoryTrackerInterface) service).getService()); 172 context.unbindService(this); 173 if (andThen != null) andThen.run(); 174 } 175 176 public void onServiceDisconnected(ComponentName className) { 177 } 178 }; 179 Log.v(TAG, "attempting to bind to memory tracker"); 180 context.bindService(new Intent(context, MemoryTracker.class), 181 connection, Context.BIND_AUTO_CREATE); 182 } 183 } 184