Daniel Sandler | b9eb286 | 2013-06-14 20:17:30 -0400 | [diff] [blame] | 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 | |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 17 | package com.android.launcher3; |
| 18 | |
| 19 | import android.app.Activity; |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 20 | import android.content.ComponentName; |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 21 | import android.content.Context; |
| 22 | import android.content.Intent; |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 23 | import android.content.ServiceConnection; |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 24 | import android.content.pm.PackageManager; |
| 25 | import android.net.Uri; |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 26 | import android.os.*; |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 27 | import android.util.Log; |
| 28 | |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 29 | import java.io.*; |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 30 | import java.util.ArrayList; |
Daniel Sandler | 0adfc5a | 2013-07-07 17:16:49 -0500 | [diff] [blame] | 31 | import java.util.Arrays; |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 32 | import java.util.zip.ZipEntry; |
| 33 | import java.util.zip.ZipOutputStream; |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 34 | |
| 35 | public class MemoryDumpActivity extends Activity { |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 36 | private static final String TAG = "MemoryDumpActivity"; |
| 37 | |
| 38 | @Override |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 39 | public void onCreate(Bundle savedInstanceState) { |
| 40 | super.onCreate(savedInstanceState); |
| 41 | } |
| 42 | |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 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; |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 50 | try { |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 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>(); |
Daniel Sandler | 4de7f73 | 2013-07-02 14:16:04 -0500 | [diff] [blame] | 87 | final int myPid = android.os.Process.myPid(); |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 88 | |
Daniel Sandler | 0adfc5a | 2013-07-07 17:16:49 -0500 | [diff] [blame] | 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) { |
Daniel Sandler | 4de7f73 | 2013-07-02 14:16:04 -0500 | [diff] [blame] | 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 | } |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 112 | } |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 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(); |
Daniel Sandler | 4de7f73 | 2013-07-02 14:16:04 -0500 | [diff] [blame] | 122 | shareIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("Launcher memory dump (%d)", myPid)); |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 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); |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | @Override |
| 141 | public void onStart() { |
| 142 | super.onStart(); |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 143 | |
Daniel Sandler | f8577a3 | 2013-06-26 14:04:59 -0400 | [diff] [blame] | 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) { |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 157 | final ServiceConnection connection = new ServiceConnection() { |
| 158 | public void onServiceConnected(ComponentName className, IBinder service) { |
| 159 | Log.v(TAG, "service connected, dumping..."); |
Daniel Sandler | f8577a3 | 2013-06-26 14:04:59 -0400 | [diff] [blame] | 160 | dumpHprofAndShare(context, |
| 161 | ((MemoryTracker.MemoryTrackerInterface) service).getService()); |
| 162 | context.unbindService(this); |
| 163 | if (andThen != null) andThen.run(); |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | public void onServiceDisconnected(ComponentName className) { |
| 167 | } |
| 168 | }; |
| 169 | Log.v(TAG, "attempting to bind to memory tracker"); |
Daniel Sandler | f8577a3 | 2013-06-26 14:04:59 -0400 | [diff] [blame] | 170 | context.bindService(new Intent(context, MemoryTracker.class), |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 171 | connection, Context.BIND_AUTO_CREATE); |
Daniel Sandler | a127b7a | 2013-06-17 14:25:46 -0400 | [diff] [blame] | 172 | } |
Daniel Sandler | 8540bb8 | 2013-06-26 01:39:02 -0400 | [diff] [blame] | 173 | } |