Skip to content
Snippets Groups Projects
Commit 6a5ccaef authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add parser for dumpsys meminfo --checkin PROCESS"

parents 238ffa86 39ba7c34
No related branches found
No related tags found
No related merge requests found
/*
* Copyright (C) 2018 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.
*/
package com.android.loganalysis.item;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
/**
* An {@link IItem} used to store output from `dumpsys meminfo --checkin PROCESS` where PROCESS is
* from the output of `dumpsys meminfo`. Data is stored as a map of categories to a map of
* measurement types to values.
*/
public class DumpsysProcessMeminfoItem extends GenericMapItem<Map<String, Long>> {
// Should match value from ActivityThread
public static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4;
// Default Categories
public static final String NATIVE = "NATIVE";
public static final String DALVIK = "DALVIK";
public static final String OTHER = "OTHER";
public static final String TOTAL = "TOTAL";
// Memory Measurement Types
public static final String PSS = "PSS";
public static final String SWAPPABLE_PSS = "SWAPPABLE_PSS";
public static final String SHARED_DIRTY = "SHARED_DIRTY";
public static final String SHARED_CLEAN = "SHARED_CLEAN";
public static final String PRIVATE_DIRTY = "PRIVATE_DIRTY";
public static final String PRIVATE_CLEAN = "PRIVATE_CLEAN";
public static final String SWAPPED_OUT = "SWAPPED_OUT";
public static final String SWAPPED_OUT_PSS = "SWAPPED_OUT_PSS";
// NATIVE, DALVIK, TOTAL only
public static final String MAX = "MAX";
public static final String ALLOCATED = "ALLOCATED";
public static final String FREE = "FREE";
public static final String[] MAIN_OUTPUT_ORDER = {
MAX,
ALLOCATED,
FREE,
PSS,
SWAPPABLE_PSS,
SHARED_DIRTY,
SHARED_CLEAN,
PRIVATE_DIRTY,
PRIVATE_CLEAN,
SWAPPED_OUT,
SWAPPED_OUT_PSS
};
public static final String[] OTHER_OUTPUT_ORDER = {
PSS,
SWAPPABLE_PSS,
SHARED_DIRTY,
SHARED_CLEAN,
PRIVATE_DIRTY,
PRIVATE_CLEAN,
SWAPPED_OUT,
SWAPPED_OUT_PSS
};
private int mPid;
private String mProcessName;
public DumpsysProcessMeminfoItem() {
this.put(NATIVE, new HashMap<>());
this.put(DALVIK, new HashMap<>());
this.put(OTHER, new HashMap<>());
this.put(TOTAL, new HashMap<>());
}
/** Get the pid */
public int getPid() {
return mPid;
}
/** Set the pid */
public void setPid(int pid) {
mPid = pid;
}
/** Get the process name */
public String getProcessName() {
return mProcessName;
}
/** Set the process name */
public void setProcessName(String processName) {
mProcessName = processName;
}
/** {@inheritDoc} */
@Override
public JSONObject toJson() {
JSONObject result = super.toJson();
try {
result.put("pid", mPid);
result.put("process_name", mProcessName);
} catch (JSONException e) {
//ignore
}
return result;
}
}
/*
* Copyright (C) 2018 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.
*/
package com.android.loganalysis.parser;
import com.android.loganalysis.item.DumpsysProcessMeminfoItem;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* An {@link IParser} used to parse output from `dumpsys meminfo --checkin PROCESS` where PROCESS is
* from the output of `dumpsys meminfo`. Data is stored as a map of categories to a map of
* measurement types to values. Format is from {@link android.app.ActivityThread#dumpMemInfoTable}.
*/
public class DumpsysProcessMeminfoParser implements IParser {
// Order is VERSION,PID,NAME,[native,dalvik,other,total]{11},[name,val{8}]*
private static final Pattern MEMINFO_OUTPUT =
Pattern.compile("(\\d+),(\\d+),([^,]+),((?:(?:N/A|\\d+),){44})(.*)");
// Matches the ending [name,val{8}]
private static final Pattern MEMINFO_ADDITIONAL_OUTPUT =
Pattern.compile("([^,]+),((?:(?:N/A|\\d+),){8})");
// Matches a value with comma
private static final Pattern MEMINFO_VALUE = Pattern.compile("(N/A|\\d+),");
@Override
public DumpsysProcessMeminfoItem parse(List<String> lines) {
DumpsysProcessMeminfoItem item = new DumpsysProcessMeminfoItem();
for (String line : lines) {
Matcher m = MEMINFO_OUTPUT.matcher(line);
if (!m.matches()) continue;
try {
item.setPid(Integer.parseInt(m.group(2)));
} catch (NumberFormatException e) {
// skip
}
item.setProcessName(m.group(3));
// parse memory info main areas
String mainValues = m.group(4);
Matcher mainMatcher = MEMINFO_VALUE.matcher(mainValues);
Map<String, Long> nativeData = item.get(DumpsysProcessMeminfoItem.NATIVE);
Map<String, Long> dalvikData = item.get(DumpsysProcessMeminfoItem.DALVIK);
Map<String, Long> otherData = item.get(DumpsysProcessMeminfoItem.OTHER);
Map<String, Long> totalData = item.get(DumpsysProcessMeminfoItem.TOTAL);
for (int i = 0; i < DumpsysProcessMeminfoItem.MAIN_OUTPUT_ORDER.length; i++) {
String curMeasurement = DumpsysProcessMeminfoItem.MAIN_OUTPUT_ORDER[i];
parseNextValue(mainMatcher, nativeData, curMeasurement);
parseNextValue(mainMatcher, dalvikData, curMeasurement);
parseNextValue(mainMatcher, otherData, curMeasurement);
parseNextValue(mainMatcher, totalData, curMeasurement);
}
String additionalData = m.group(5);
Matcher additionalMatcher = MEMINFO_ADDITIONAL_OUTPUT.matcher(additionalData);
// parse memory info other areas
while (additionalMatcher.find()) {
try {
String curLabel = additionalMatcher.group(1);
Matcher additionalValueMatcher =
MEMINFO_VALUE.matcher(additionalMatcher.group(2));
Map<String, Long> curData = new HashMap<>();
for (int i = 0; i < DumpsysProcessMeminfoItem.OTHER_OUTPUT_ORDER.length; i++) {
String curMeasurement = DumpsysProcessMeminfoItem.OTHER_OUTPUT_ORDER[i];
parseNextValue(additionalValueMatcher, curData, curMeasurement);
}
item.put(curLabel, curData);
} catch (ArrayIndexOutOfBoundsException e) {
break;
}
}
}
return item;
}
private void parseNextValue(Matcher m, Map<String, Long> output, String key) {
if (!m.find()) return;
String value = m.group(1);
if ("N/A".equals(value)) return;
try {
output.put(key, Long.parseLong(value));
} catch (NumberFormatException e) {
// skip
}
}
}
...@@ -45,6 +45,7 @@ import com.android.loganalysis.parser.DumpsysBatteryStatsParserTest; ...@@ -45,6 +45,7 @@ import com.android.loganalysis.parser.DumpsysBatteryStatsParserTest;
import com.android.loganalysis.parser.DumpsysPackageStatsParserTest; import com.android.loganalysis.parser.DumpsysPackageStatsParserTest;
import com.android.loganalysis.parser.DumpsysParserTest; import com.android.loganalysis.parser.DumpsysParserTest;
import com.android.loganalysis.parser.DumpsysProcStatsParserTest; import com.android.loganalysis.parser.DumpsysProcStatsParserTest;
import com.android.loganalysis.parser.DumpsysProcessMeminfoParserTest;
import com.android.loganalysis.parser.DumpsysWifiStatsParserTest; import com.android.loganalysis.parser.DumpsysWifiStatsParserTest;
import com.android.loganalysis.parser.DvmLockSampleParserTest; import com.android.loganalysis.parser.DvmLockSampleParserTest;
import com.android.loganalysis.parser.EventsLogParserTest; import com.android.loganalysis.parser.EventsLogParserTest;
...@@ -130,6 +131,7 @@ import org.junit.runners.Suite.SuiteClasses; ...@@ -130,6 +131,7 @@ import org.junit.runners.Suite.SuiteClasses;
DumpsysBatteryStatsParserTest.class, DumpsysBatteryStatsParserTest.class,
DumpsysPackageStatsParserTest.class, DumpsysPackageStatsParserTest.class,
DumpsysParserTest.class, DumpsysParserTest.class,
DumpsysProcessMeminfoParserTest.class,
DumpsysProcStatsParserTest.class, DumpsysProcStatsParserTest.class,
DumpsysWifiStatsParserTest.class, DumpsysWifiStatsParserTest.class,
DvmLockSampleParserTest.class, DvmLockSampleParserTest.class,
......
/*
* Copyright (C) 2018 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.
*/
package com.android.loganalysis.parser;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.android.loganalysis.item.DumpsysProcessMeminfoItem;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
/** Unit tests for {@link DumpsysProcessMeminfoParser} */
public class DumpsysProcessMeminfoParserTest {
private static final String TEST_INPUT =
"time,28506638,177086152\n"
+ "4,938,system_server,11,22,N/A,44,0,0,N/A,0,0,0,N/A,0,27613,14013,176602,"
+ "218228,0,0,122860,122860,1512,1412,5740,8664,0,0,154924,154924,27568,"
+ "13972,11916,53456,0,0,123008,123008,0,0,0,0,0,0,0,0,Dalvik Other,3662,0,"
+ "104,0,3660,0,0,0,Stack,1576,0,8,0,1576,0,0,0,Cursor,0,0,0,0,0,0,0,0,"
+ "Ashmem,156,0,20,0,148,0,0,0,Gfx dev,100,0,48,0,76,0,0,0,Other dev,116,0,"
+ "164,0,0,96,0,0,.so mmap,7500,2680,3984,21864,904,2680,0,0,.jar mmap,0,0,0,"
+ "0,0,0,0,0,.apk mmap,72398,71448,0,11736,0,71448,0,0,.ttf mmap,0,0,0,0,0,0,"
+ "0,0,.dex mmap,76874,46000,0,83644,40,46000,0,0,.oat mmap,8127,2684,64,"
+ "26652,0,2684,0,0,.art mmap,1991,48,972,10004,1544,48,0,0,Other mmap,137,0,"
+ "44,1024,4,52,0,0,EGL mtrack,0,0,0,0,0,0,0,0,GL mtrack,111,222,333,444,555,"
+ "666,777,888,";
private static final String INVALID_TEST_INPUT = "RANDOM,TEST,DATA\n234235345345";
// Test that normal input is parsed
@Test
public void testDumpsysProcessMeminfoParser() {
List<String> inputBlock = Arrays.asList(TEST_INPUT.split("\n"));
DumpsysProcessMeminfoItem dump = new DumpsysProcessMeminfoParser().parse(inputBlock);
assertEquals(938, dump.getPid());
assertEquals("system_server", dump.getProcessName());
assertEquals(
Long.valueOf(11L),
dump.get(DumpsysProcessMeminfoItem.NATIVE).get(DumpsysProcessMeminfoItem.MAX));
assertEquals(
Long.valueOf(22L),
dump.get(DumpsysProcessMeminfoItem.DALVIK).get(DumpsysProcessMeminfoItem.MAX));
assertFalse(
dump.get(DumpsysProcessMeminfoItem.OTHER)
.containsKey(DumpsysProcessMeminfoItem.MAX));
assertEquals(
Long.valueOf(44L),
dump.get(DumpsysProcessMeminfoItem.TOTAL).get(DumpsysProcessMeminfoItem.MAX));
assertEquals(
Long.valueOf(218228L),
dump.get(DumpsysProcessMeminfoItem.TOTAL).get(DumpsysProcessMeminfoItem.PSS));
assertEquals(
Long.valueOf(3662L), dump.get("Dalvik Other").get(DumpsysProcessMeminfoItem.PSS));
assertEquals(Long.valueOf(111L), dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.PSS));
assertEquals(
Long.valueOf(222L),
dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.SWAPPABLE_PSS));
assertEquals(
Long.valueOf(333L),
dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.SHARED_DIRTY));
assertEquals(
Long.valueOf(444L),
dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.SHARED_CLEAN));
assertEquals(
Long.valueOf(555L),
dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.PRIVATE_DIRTY));
assertEquals(
Long.valueOf(666L),
dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.PRIVATE_CLEAN));
assertEquals(
Long.valueOf(777L),
dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.SWAPPED_OUT));
assertEquals(
Long.valueOf(888L),
dump.get("GL mtrack").get(DumpsysProcessMeminfoItem.SWAPPED_OUT_PSS));
}
// Test that the parser does not crash on invalid input and returns empty data
@Test
public void testDumpsysProcessMeminfoParserInvalid() {
List<String> inputBlock = Arrays.asList(INVALID_TEST_INPUT.split("\n"));
DumpsysProcessMeminfoItem dump = new DumpsysProcessMeminfoParser().parse(inputBlock);
assertNull(dump.getProcessName());
assertTrue(dump.get(DumpsysProcessMeminfoItem.TOTAL).isEmpty());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment