Skip to content

Commit 8103890

Browse files
author
Dianne Hackborn
committed
Improve debugging for issue #7586414: AlarmManager wakelocks held
In alarm manager, print a summary of the top 10 alarms by time being executed. Keep track of execution time (and wake count) of each type of alarm for each application so this can be printed in the summary (and used to compute the top 10 alarms). Rework how the alarm summary stats are tracked so that we don't need to hold on to the full Intent for each stat and can get the Intent information at the time the alarm is sent rather than waiting for whatever Intent comes back in the result. Also in the battery stats: sort the kernel wake locks by time, add a new section showing all partial wake locks across all applications sorted by time. Finally a new LocalLog class that is used by AlarmManager to log important warning messages, so these can also be later found in its dumpsys output. Change-Id: Icc07810053e60fb623a49937e696819cb8352b06
1 parent 9a0fbd4 commit 8103890

File tree

7 files changed

+400
-52
lines changed

7 files changed

+400
-52
lines changed

core/java/android/app/ActivityManagerNative.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,21 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
17011701
return true;
17021702
}
17031703

1704+
case GET_INTENT_FOR_INTENT_SENDER_TRANSACTION: {
1705+
data.enforceInterface(IActivityManager.descriptor);
1706+
IIntentSender r = IIntentSender.Stub.asInterface(
1707+
data.readStrongBinder());
1708+
Intent intent = getIntentForIntentSender(r);
1709+
reply.writeNoException();
1710+
if (intent != null) {
1711+
reply.writeInt(1);
1712+
intent.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
1713+
} else {
1714+
reply.writeInt(0);
1715+
}
1716+
return true;
1717+
}
1718+
17041719
case UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION: {
17051720
data.enforceInterface(IActivityManager.descriptor);
17061721
Configuration config = Configuration.CREATOR.createFromParcel(data);
@@ -3977,6 +3992,20 @@ public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteExcep
39773992
return res;
39783993
}
39793994

3995+
public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException {
3996+
Parcel data = Parcel.obtain();
3997+
Parcel reply = Parcel.obtain();
3998+
data.writeInterfaceToken(IActivityManager.descriptor);
3999+
data.writeStrongBinder(sender.asBinder());
4000+
mRemote.transact(GET_INTENT_FOR_INTENT_SENDER_TRANSACTION, data, reply, 0);
4001+
reply.readException();
4002+
Intent res = reply.readInt() != 0
4003+
? Intent.CREATOR.createFromParcel(reply) : null;
4004+
data.recycle();
4005+
reply.recycle();
4006+
return res;
4007+
}
4008+
39804009
public void updatePersistentConfiguration(Configuration values) throws RemoteException
39814010
{
39824011
Parcel data = Parcel.obtain();

core/java/android/app/IActivityManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ public void setPackageAskScreenCompat(String packageName, boolean ask)
341341

342342
public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteException;
343343

344+
public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException;
345+
344346
public void updatePersistentConfiguration(Configuration values) throws RemoteException;
345347

346348
public long[] getProcessPss(int[] pids) throws RemoteException;
@@ -621,4 +623,5 @@ private WaitResult(Parcel source) {
621623
int REQUEST_BUG_REPORT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+157;
622624
int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158;
623625
int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159;
626+
int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+160;
624627
}

core/java/android/app/PendingIntent.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,20 @@ public boolean isActivity() {
789789
}
790790
}
791791

792+
/**
793+
* @hide
794+
* Return the Intent of this PendingIntent.
795+
*/
796+
public Intent getIntent() {
797+
try {
798+
return ActivityManagerNative.getDefault()
799+
.getIntentForIntentSender(mTarget);
800+
} catch (RemoteException e) {
801+
// Should never happen.
802+
return null;
803+
}
804+
}
805+
792806
/**
793807
* Comparison operator on two PendingIntent objects, such that true
794808
* is returned then they both represent the same operation from the

core/java/android/os/BatteryStats.java

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.io.PrintWriter;
2020
import java.util.ArrayList;
21+
import java.util.Collections;
22+
import java.util.Comparator;
2123
import java.util.Formatter;
2224
import java.util.List;
2325
import java.util.Map;
@@ -1127,8 +1129,10 @@ private static final String printWakeLock(StringBuilder sb, Timer timer,
11271129
if (totalTimeMillis != 0) {
11281130
sb.append(linePrefix);
11291131
formatTimeMs(sb, totalTimeMillis);
1130-
if (name != null) sb.append(name);
1131-
sb.append(' ');
1132+
if (name != null) {
1133+
sb.append(name);
1134+
sb.append(' ');
1135+
}
11321136
sb.append('(');
11331137
sb.append(count);
11341138
sb.append(" times)");
@@ -1440,8 +1444,21 @@ public final void dumpCheckinLocked(PrintWriter pw, int which, int reqUid) {
14401444
}
14411445
}
14421446

1447+
static final class TimerEntry {
1448+
final String mName;
1449+
final int mId;
1450+
final BatteryStats.Timer mTimer;
1451+
final long mTime;
1452+
TimerEntry(String name, int id, BatteryStats.Timer timer, long time) {
1453+
mName = name;
1454+
mId = id;
1455+
mTimer = timer;
1456+
mTime = time;
1457+
}
1458+
}
1459+
14431460
@SuppressWarnings("unused")
1444-
public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUid) {
1461+
public final void dumpLocked(PrintWriter pw, String prefix, final int which, int reqUid) {
14451462
final long rawUptime = SystemClock.uptimeMillis() * 1000;
14461463
final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
14471464
final long batteryUptime = getBatteryUptime(rawUptime);
@@ -1516,19 +1533,43 @@ public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUi
15161533
long txTotal = 0;
15171534
long fullWakeLockTimeTotalMicros = 0;
15181535
long partialWakeLockTimeTotalMicros = 0;
1519-
1536+
1537+
final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
1538+
@Override
1539+
public int compare(TimerEntry lhs, TimerEntry rhs) {
1540+
long lhsTime = lhs.mTime;
1541+
long rhsTime = rhs.mTime;
1542+
if (lhsTime < rhsTime) {
1543+
return 1;
1544+
}
1545+
if (lhsTime > rhsTime) {
1546+
return -1;
1547+
}
1548+
return 0;
1549+
}
1550+
};
1551+
15201552
if (reqUid < 0) {
15211553
Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
15221554
if (kernelWakelocks.size() > 0) {
1555+
final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
15231556
for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
1524-
1557+
BatteryStats.Timer timer = ent.getValue();
1558+
long totalTimeMillis = computeWakeLock(timer, batteryRealtime, which);
1559+
if (totalTimeMillis > 0) {
1560+
timers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
1561+
}
1562+
}
1563+
Collections.sort(timers, timerComparator);
1564+
for (int i=0; i<timers.size(); i++) {
1565+
TimerEntry timer = timers.get(i);
15251566
String linePrefix = ": ";
15261567
sb.setLength(0);
15271568
sb.append(prefix);
15281569
sb.append(" Kernel Wake lock ");
1529-
sb.append(ent.getKey());
1530-
linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which,
1531-
linePrefix);
1570+
sb.append(timer.mName);
1571+
linePrefix = printWakeLock(sb, timer.mTimer, batteryRealtime, null,
1572+
which, linePrefix);
15321573
if (!linePrefix.equals(": ")) {
15331574
sb.append(" realtime");
15341575
// Only print out wake locks that were held
@@ -1537,7 +1578,9 @@ public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUi
15371578
}
15381579
}
15391580
}
1540-
1581+
1582+
final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
1583+
15411584
for (int iu = 0; iu < NU; iu++) {
15421585
Uid u = uidStats.valueAt(iu);
15431586
rxTotal += u.getTcpBytesReceived(which);
@@ -1557,8 +1600,18 @@ public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUi
15571600

15581601
Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
15591602
if (partialWakeTimer != null) {
1560-
partialWakeLockTimeTotalMicros += partialWakeTimer.getTotalTimeLocked(
1603+
long totalTimeMicros = partialWakeTimer.getTotalTimeLocked(
15611604
batteryRealtime, which);
1605+
if (totalTimeMicros > 0) {
1606+
if (reqUid < 0) {
1607+
// Only show the ordered list of all wake
1608+
// locks if the caller is not asking for data
1609+
// about a specific uid.
1610+
timers.add(new TimerEntry(ent.getKey(), u.getUid(),
1611+
partialWakeTimer, totalTimeMicros));
1612+
}
1613+
partialWakeLockTimeTotalMicros += totalTimeMicros;
1614+
}
15621615
}
15631616
}
15641617
}
@@ -1571,7 +1624,7 @@ public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUi
15711624
sb.append(prefix);
15721625
sb.append(" Total full wakelock time: "); formatTimeMs(sb,
15731626
(fullWakeLockTimeTotalMicros + 500) / 1000);
1574-
sb.append(", Total partial waklock time: "); formatTimeMs(sb,
1627+
sb.append(", Total partial wakelock time: "); formatTimeMs(sb,
15751628
(partialWakeLockTimeTotalMicros + 500) / 1000);
15761629
pw.println(sb.toString());
15771630

@@ -1676,9 +1729,26 @@ public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUi
16761729
pw.println(getDischargeAmountScreenOnSinceCharge());
16771730
pw.print(prefix); pw.print(" Amount discharged while screen off: ");
16781731
pw.println(getDischargeAmountScreenOffSinceCharge());
1679-
pw.println(" ");
1732+
pw.println();
1733+
}
1734+
1735+
if (timers.size() > 0) {
1736+
Collections.sort(timers, timerComparator);
1737+
pw.print(prefix); pw.println(" All partial wake locks:");
1738+
for (int i=0; i<timers.size(); i++) {
1739+
TimerEntry timer = timers.get(i);
1740+
sb.setLength(0);
1741+
sb.append(" Wake lock #");
1742+
sb.append(timer.mId);
1743+
sb.append(" ");
1744+
sb.append(timer.mName);
1745+
printWakeLock(sb, timer.mTimer, batteryRealtime, null, which, ": ");
1746+
sb.append(" realtime");
1747+
pw.println(sb.toString());
1748+
}
1749+
timers.clear();
1750+
pw.println();
16801751
}
1681-
16821752

16831753
for (int iu=0; iu<NU; iu++) {
16841754
final int uid = uidStats.keyAt(iu);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (C) 2012 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.internal.util;
18+
19+
import java.io.PrintWriter;
20+
import java.util.ArrayList;
21+
22+
import android.util.Slog;
23+
24+
/**
25+
* Helper class for logging serious issues, which also keeps a small
26+
* snapshot of the logged events that can be printed later, such as part
27+
* of a system service's dumpsys output.
28+
* @hide
29+
*/
30+
public class LocalLog {
31+
private final String mTag;
32+
private final int mMaxLines = 20;
33+
private final ArrayList<String> mLines = new ArrayList<String>(mMaxLines);
34+
35+
public LocalLog(String tag) {
36+
mTag = tag;
37+
}
38+
39+
public void w(String msg) {
40+
synchronized (mLines) {
41+
Slog.w(mTag, msg);
42+
if (mLines.size() >= mMaxLines) {
43+
mLines.remove(0);
44+
}
45+
mLines.add(msg);
46+
}
47+
}
48+
49+
public boolean dump(PrintWriter pw, String header, String prefix) {
50+
synchronized (mLines) {
51+
if (mLines.size() <= 0) {
52+
return false;
53+
}
54+
if (header != null) {
55+
pw.println(header);
56+
}
57+
for (int i=0; i<mLines.size(); i++) {
58+
if (prefix != null) {
59+
pw.print(prefix);
60+
}
61+
pw.println(mLines.get(i));
62+
}
63+
return true;
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)