From: Ng Zhi An Date: Wed, 31 Jan 2018 01:12:39 +0000 (-0800) Subject: Log app start to statsd X-Git-Tag: android-x86-9.0-r1~217^2~1^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=bbefdec6a445f93ea044df50e72219d9f1470c8e;p=android-x86%2Fframeworks-base.git Log app start to statsd Bug: 72713338 Test: manual, open app, logcat -b stats Change-Id: Ic5dc74a637601df443de29eb6f13bd63dd03c6e7 --- diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 66f0592e36e5..e2ceb31ceef8 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -32,6 +32,8 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_T import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.MemoryStatUtil.MemoryStat; +import static com.android.server.am.MemoryStatUtil.readMemoryStatFromMemcg; import android.content.Context; import android.metrics.LogMaker; @@ -67,6 +69,7 @@ class ActivityMetricsLogger { private static final long INVALID_START_TIME = -1; private static final int MSG_CHECK_VISIBILITY = 0; + private static final int MSG_LOG_APP_START_MEMORY_STATE_CAPTURE = 1; // Preallocated strings we are sending to tron, so we don't have to allocate a new one every // time we log. @@ -102,6 +105,9 @@ class ActivityMetricsLogger { final SomeArgs args = (SomeArgs) msg.obj; checkVisibility((TaskRecord) args.arg1, (ActivityRecord) args.arg2); break; + case MSG_LOG_APP_START_MEMORY_STATE_CAPTURE: + logAppStartMemoryStateCapture((StackTransitionInfo) msg.obj); + break; } } }; @@ -187,10 +193,7 @@ class ActivityMetricsLogger { * @param launchedActivity the activity that is being launched */ void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity) { - final ProcessRecord processRecord = launchedActivity != null - ? mSupervisor.mService.mProcessNames.get(launchedActivity.processName, - launchedActivity.appInfo.uid) - : null; + final ProcessRecord processRecord = findProcessForActivity(launchedActivity); final boolean processRunning = processRecord != null; // We consider this a "process switch" if the process of the activity that gets launched @@ -492,6 +495,7 @@ class ActivityMetricsLogger { info.bindApplicationDelayMs, info.windowsDrawnDelayMs, launchToken); + mHandler.obtainMessage(MSG_LOG_APP_START_MEMORY_STATE_CAPTURE, info).sendToTarget(); } } @@ -548,4 +552,38 @@ class ActivityMetricsLogger { } return -1; } + + private void logAppStartMemoryStateCapture(StackTransitionInfo info) { + final ProcessRecord processRecord = findProcessForActivity(info.launchedActivity); + if (processRecord == null) { + if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null"); + return; + } + + final int pid = processRecord.pid; + final int uid = info.launchedActivity.appInfo.uid; + final MemoryStat memoryStat = readMemoryStatFromMemcg(uid, pid); + if (memoryStat == null) { + if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture memoryStat null"); + return; + } + + StatsLog.write( + StatsLog.APP_START_MEMORY_STATE_CAPTURED, + uid, + info.launchedActivity.processName, + info.launchedActivity.info.name, + memoryStat.pgfault, + memoryStat.pgmajfault, + memoryStat.rssInBytes, + memoryStat.cacheInBytes, + memoryStat.swapInBytes); + } + + private ProcessRecord findProcessForActivity(ActivityRecord launchedActivity) { + return launchedActivity != null + ? mSupervisor.mService.mProcessNames.get(launchedActivity.processName, + launchedActivity.appInfo.uid) + : null; + } } diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java new file mode 100644 index 000000000000..d97c2a24ef1e --- /dev/null +++ b/services/core/java/com/android/server/am/MemoryStatUtil.java @@ -0,0 +1,109 @@ +/* + * 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.server.am; + +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.annotation.Nullable; +import android.os.FileUtils; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Static utility methods related to {@link MemoryStat}. + */ +final class MemoryStatUtil { + private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM; + + /** Path to memory stat file for logging app start memory state */ + private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat"; + + private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)"); + private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)"); + private static final Pattern RSS_IN_BYTES = Pattern.compile("total_rss (\\d+)"); + private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)"); + private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)"); + + private MemoryStatUtil() {} + + /** + * Reads memory.stat of a process from memcg. + */ + static @Nullable MemoryStat readMemoryStatFromMemcg(int uid, int pid) { + final String memoryStatPath = String.format(MEMORY_STAT_FILE_FMT, uid, pid); + final File memoryStatFile = new File(memoryStatPath); + if (!memoryStatFile.exists()) { + if (DEBUG_METRICS) Slog.i(TAG, memoryStatPath + " not found"); + return null; + } + + try { + final String memoryStatContents = FileUtils.readTextFile( + memoryStatFile, 0 /* max */, null /* ellipsis */); + return parseMemoryStat(memoryStatContents); + } catch (IOException e) { + Slog.e(TAG, "Failed to read file:", e); + return null; + } + } + + /** + * Parses relevant statistics out from the contents of a memory.stat file in memcg. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + static @Nullable MemoryStat parseMemoryStat(String memoryStatContents) { + MemoryStat memoryStat = new MemoryStat(); + if (memoryStatContents == null) { + return memoryStat; + } + + Matcher m; + m = PGFAULT.matcher(memoryStatContents); + memoryStat.pgfault = m.find() ? Long.valueOf(m.group(1)) : 0; + m = PGMAJFAULT.matcher(memoryStatContents); + memoryStat.pgmajfault = m.find() ? Long.valueOf(m.group(1)) : 0; + m = RSS_IN_BYTES.matcher(memoryStatContents); + memoryStat.rssInBytes = m.find() ? Long.valueOf(m.group(1)) : 0; + m = CACHE_IN_BYTES.matcher(memoryStatContents); + memoryStat.cacheInBytes = m.find() ? Long.valueOf(m.group(1)) : 0; + m = SWAP_IN_BYTES.matcher(memoryStatContents); + memoryStat.swapInBytes = m.find() ? Long.valueOf(m.group(1)) : 0; + return memoryStat; + } + + static final class MemoryStat { + /** Number of page faults */ + long pgfault; + /** Number of major page faults */ + long pgmajfault; + /** Number of bytes of anonymous and swap cache memory */ + long rssInBytes; + /** Number of bytes of page cache memory */ + long cacheInBytes; + /** Number of bytes of swap usage */ + long swapInBytes; + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java new file mode 100644 index 000000000000..a7e38104f5be --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java @@ -0,0 +1,95 @@ +/* + * 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.server.am; + +import static com.android.server.am.MemoryStatUtil.parseMemoryStat; +import static com.android.server.am.MemoryStatUtil.MemoryStat; + +import static org.junit.Assert.assertEquals; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MemoryStatUtilTest { + private String MEMORY_STAT_CONTENTS = String.join( + "\n", + "cache 96", // keep different from total_cache to catch reading wrong value + "rss 97", // keep different from total_rss to catch reading wrong value + "rss_huge 0", + "mapped_file 524288", + "writeback 0", + "swap 95", // keep different from total_rss to catch reading wrong value + "pgpgin 16717", + "pgpgout 5037", + "pgfault 99", // keep different from total_pgfault to catch reading wrong value + "pgmajfault 98", // keep different from total_pgmajfault to catch reading wrong value + "inactive_anon 503808", + "active_anon 46309376", + "inactive_file 876544", + "active_file 81920", + "unevictable 0", + "hierarchical_memory_limit 18446744073709551615", + "hierarchical_memsw_limit 18446744073709551615", + "total_cache 4", + "total_rss 3", + "total_rss_huge 0", + "total_mapped_file 524288", + "total_writeback 0", + "total_swap 5", + "total_pgpgin 16717", + "total_pgpgout 5037", + "total_pgfault 1", + "total_pgmajfault 2", + "total_inactive_anon 503808", + "total_active_anon 46309376", + "total_inactive_file 876544", + "total_active_file 81920", + "total_unevictable 0"); + + + @Test + public void testParseMemoryStat_parsesCorrectValues() throws Exception { + MemoryStat stat = parseMemoryStat(MEMORY_STAT_CONTENTS); + assertEquals(stat.pgfault, 1); + assertEquals(stat.pgmajfault, 2); + assertEquals(stat.rssInBytes, 3); + assertEquals(stat.cacheInBytes, 4); + assertEquals(stat.swapInBytes, 5); + } + + @Test + public void testParseMemoryStat_emptyMemoryStatContents() throws Exception { + MemoryStat stat = parseMemoryStat(""); + assertEquals(stat.pgfault, 0); + assertEquals(stat.pgmajfault, 0); + assertEquals(stat.rssInBytes, 0); + assertEquals(stat.cacheInBytes, 0); + assertEquals(stat.swapInBytes, 0); + + stat = parseMemoryStat(null); + assertEquals(stat.pgfault, 0); + assertEquals(stat.pgmajfault, 0); + assertEquals(stat.rssInBytes, 0); + assertEquals(stat.cacheInBytes, 0); + assertEquals(stat.swapInBytes, 0); + } +}