You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
9.4 KiB
299 lines
9.4 KiB
4 months ago
|
/*
|
||
|
* Copyright (C) 2008 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
import java.io.Serializable;
|
||
|
import java.io.IOException;
|
||
|
import java.io.BufferedReader;
|
||
|
import java.io.InputStreamReader;
|
||
|
import java.io.InputStream;
|
||
|
import java.io.OutputStream;
|
||
|
import java.util.List;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Arrays;
|
||
|
|
||
|
/**
|
||
|
* Memory usage information.
|
||
|
*/
|
||
|
class MemoryUsage implements Serializable {
|
||
|
|
||
|
private static final long serialVersionUID = 0;
|
||
|
|
||
|
static final MemoryUsage NOT_AVAILABLE = new MemoryUsage();
|
||
|
|
||
|
static int errorCount = 0;
|
||
|
|
||
|
// These values are in 1kB increments (not 4kB like you'd expect).
|
||
|
final int nativeSharedPages;
|
||
|
final int javaSharedPages;
|
||
|
final int otherSharedPages;
|
||
|
final int nativePrivatePages;
|
||
|
final int javaPrivatePages;
|
||
|
final int otherPrivatePages;
|
||
|
|
||
|
final int allocCount;
|
||
|
final int allocSize;
|
||
|
final int freedCount;
|
||
|
final int freedSize;
|
||
|
final long nativeHeapSize;
|
||
|
|
||
|
public MemoryUsage(String line) {
|
||
|
String[] parsed = line.split(",");
|
||
|
|
||
|
nativeSharedPages = Integer.parseInt(parsed[1]);
|
||
|
javaSharedPages = Integer.parseInt(parsed[2]);
|
||
|
otherSharedPages = Integer.parseInt(parsed[3]);
|
||
|
nativePrivatePages = Integer.parseInt(parsed[4]);
|
||
|
javaPrivatePages = Integer.parseInt(parsed[5]);
|
||
|
otherPrivatePages = Integer.parseInt(parsed[6]);
|
||
|
allocCount = Integer.parseInt(parsed[7]);
|
||
|
allocSize = Integer.parseInt(parsed[8]);
|
||
|
freedCount = Integer.parseInt(parsed[9]);
|
||
|
freedSize = Integer.parseInt(parsed[10]);
|
||
|
nativeHeapSize = Long.parseLong(parsed[11]);
|
||
|
}
|
||
|
|
||
|
MemoryUsage() {
|
||
|
nativeSharedPages = -1;
|
||
|
javaSharedPages = -1;
|
||
|
otherSharedPages = -1;
|
||
|
nativePrivatePages = -1;
|
||
|
javaPrivatePages = -1;
|
||
|
otherPrivatePages = -1;
|
||
|
|
||
|
allocCount = -1;
|
||
|
allocSize = -1;
|
||
|
freedCount = -1;
|
||
|
freedSize = -1;
|
||
|
nativeHeapSize = -1;
|
||
|
}
|
||
|
|
||
|
MemoryUsage(int nativeSharedPages,
|
||
|
int javaSharedPages,
|
||
|
int otherSharedPages,
|
||
|
int nativePrivatePages,
|
||
|
int javaPrivatePages,
|
||
|
int otherPrivatePages,
|
||
|
int allocCount,
|
||
|
int allocSize,
|
||
|
int freedCount,
|
||
|
int freedSize,
|
||
|
long nativeHeapSize) {
|
||
|
this.nativeSharedPages = nativeSharedPages;
|
||
|
this.javaSharedPages = javaSharedPages;
|
||
|
this.otherSharedPages = otherSharedPages;
|
||
|
this.nativePrivatePages = nativePrivatePages;
|
||
|
this.javaPrivatePages = javaPrivatePages;
|
||
|
this.otherPrivatePages = otherPrivatePages;
|
||
|
this.allocCount = allocCount;
|
||
|
this.allocSize = allocSize;
|
||
|
this.freedCount = freedCount;
|
||
|
this.freedSize = freedSize;
|
||
|
this.nativeHeapSize = nativeHeapSize;
|
||
|
}
|
||
|
|
||
|
MemoryUsage subtract(MemoryUsage baseline) {
|
||
|
return new MemoryUsage(
|
||
|
nativeSharedPages - baseline.nativeSharedPages,
|
||
|
javaSharedPages - baseline.javaSharedPages,
|
||
|
otherSharedPages - baseline.otherSharedPages,
|
||
|
nativePrivatePages - baseline.nativePrivatePages,
|
||
|
javaPrivatePages - baseline.javaPrivatePages,
|
||
|
otherPrivatePages - baseline.otherPrivatePages,
|
||
|
allocCount - baseline.allocCount,
|
||
|
allocSize - baseline.allocSize,
|
||
|
freedCount - baseline.freedCount,
|
||
|
freedSize - baseline.freedSize,
|
||
|
nativeHeapSize - baseline.nativeHeapSize);
|
||
|
}
|
||
|
|
||
|
int javaHeapSize() {
|
||
|
return allocSize - freedSize;
|
||
|
}
|
||
|
|
||
|
int totalHeap() {
|
||
|
return javaHeapSize() + (int) nativeHeapSize;
|
||
|
}
|
||
|
|
||
|
int javaPagesInK() {
|
||
|
return javaSharedPages + javaPrivatePages;
|
||
|
}
|
||
|
|
||
|
int nativePagesInK() {
|
||
|
return nativeSharedPages + nativePrivatePages;
|
||
|
}
|
||
|
int otherPagesInK() {
|
||
|
return otherSharedPages + otherPrivatePages;
|
||
|
}
|
||
|
|
||
|
int totalPages() {
|
||
|
return javaSharedPages + javaPrivatePages + nativeSharedPages +
|
||
|
nativePrivatePages + otherSharedPages + otherPrivatePages;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Was this information available?
|
||
|
*/
|
||
|
boolean isAvailable() {
|
||
|
return nativeSharedPages != -1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Measures baseline memory usage.
|
||
|
*/
|
||
|
static MemoryUsage baseline() {
|
||
|
return forClass(null);
|
||
|
}
|
||
|
|
||
|
private static final String CLASS_PATH = "-Xbootclasspath"
|
||
|
+ ":/system/framework/core.jar"
|
||
|
+ ":/system/framework/ext.jar"
|
||
|
+ ":/system/framework/framework.jar"
|
||
|
+ ":/system/framework/framework-tests.jar"
|
||
|
+ ":/system/framework/services.jar"
|
||
|
+ ":/system/framework/loadclass.jar";
|
||
|
|
||
|
private static final String[] GET_DIRTY_PAGES = {
|
||
|
"adb", "shell", "dalvikvm", CLASS_PATH, "LoadClass" };
|
||
|
|
||
|
/**
|
||
|
* Measures memory usage for the given class.
|
||
|
*/
|
||
|
static MemoryUsage forClass(String className) {
|
||
|
MeasureWithTimeout measurer = new MeasureWithTimeout(className);
|
||
|
|
||
|
new Thread(measurer).start();
|
||
|
|
||
|
synchronized (measurer) {
|
||
|
if (measurer.memoryUsage == null) {
|
||
|
// Wait up to 10s.
|
||
|
try {
|
||
|
measurer.wait(30000);
|
||
|
} catch (InterruptedException e) {
|
||
|
System.err.println("Interrupted waiting for measurement.");
|
||
|
e.printStackTrace();
|
||
|
return NOT_AVAILABLE;
|
||
|
}
|
||
|
|
||
|
// If it's still null.
|
||
|
if (measurer.memoryUsage == null) {
|
||
|
System.err.println("Timed out while measuring "
|
||
|
+ className + ".");
|
||
|
return NOT_AVAILABLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
System.err.println("Got memory usage for " + className + ".");
|
||
|
return measurer.memoryUsage;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static class MeasureWithTimeout implements Runnable {
|
||
|
|
||
|
final String className;
|
||
|
MemoryUsage memoryUsage = null;
|
||
|
|
||
|
MeasureWithTimeout(String className) {
|
||
|
this.className = className;
|
||
|
}
|
||
|
|
||
|
public void run() {
|
||
|
MemoryUsage measured = measure();
|
||
|
|
||
|
synchronized (this) {
|
||
|
memoryUsage = measured;
|
||
|
notifyAll();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private MemoryUsage measure() {
|
||
|
String[] commands = GET_DIRTY_PAGES;
|
||
|
if (className != null) {
|
||
|
List<String> commandList = new ArrayList<String>(
|
||
|
GET_DIRTY_PAGES.length + 1);
|
||
|
commandList.addAll(Arrays.asList(commands));
|
||
|
commandList.add(className);
|
||
|
commands = commandList.toArray(new String[commandList.size()]);
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
final Process process = Runtime.getRuntime().exec(commands);
|
||
|
|
||
|
final InputStream err = process.getErrorStream();
|
||
|
|
||
|
// Send error output to stderr.
|
||
|
Thread errThread = new Thread() {
|
||
|
@Override
|
||
|
public void run() {
|
||
|
copy(err, System.err);
|
||
|
}
|
||
|
};
|
||
|
errThread.setDaemon(true);
|
||
|
errThread.start();
|
||
|
|
||
|
BufferedReader in = new BufferedReader(
|
||
|
new InputStreamReader(process.getInputStream()));
|
||
|
String line = in.readLine();
|
||
|
if (line == null || !line.startsWith("DECAFBAD,")) {
|
||
|
System.err.println("Got bad response for " + className
|
||
|
+ ": " + line + "; command was " + Arrays.toString(commands));
|
||
|
errorCount += 1;
|
||
|
return NOT_AVAILABLE;
|
||
|
}
|
||
|
|
||
|
in.close();
|
||
|
err.close();
|
||
|
process.destroy();
|
||
|
|
||
|
return new MemoryUsage(line);
|
||
|
} catch (IOException e) {
|
||
|
System.err.println("Error getting stats for "
|
||
|
+ className + ".");
|
||
|
e.printStackTrace();
|
||
|
return NOT_AVAILABLE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Copies from one stream to another.
|
||
|
*/
|
||
|
private static void copy(InputStream in, OutputStream out) {
|
||
|
byte[] buffer = new byte[1024];
|
||
|
int read;
|
||
|
try {
|
||
|
while ((read = in.read(buffer)) > -1) {
|
||
|
out.write(buffer, 0, read);
|
||
|
}
|
||
|
} catch (IOException e) {
|
||
|
e.printStackTrace();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Measures memory usage information and stores it in the model. */
|
||
|
public static void main(String[] args) throws IOException,
|
||
|
ClassNotFoundException {
|
||
|
Root root = Root.fromFile(args[0]);
|
||
|
root.baseline = baseline();
|
||
|
for (LoadedClass loadedClass : root.loadedClasses.values()) {
|
||
|
if (loadedClass.systemClass) {
|
||
|
loadedClass.measureMemoryUsage();
|
||
|
}
|
||
|
}
|
||
|
root.toFile(args[0]);
|
||
|
}
|
||
|
}
|