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