1 /*
2  * Copyright (C) 2015 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.ahat;
18 
19 import com.android.ahat.heapdump.AhatClassObj;
20 import com.android.ahat.heapdump.AhatInstance;
21 import com.android.ahat.heapdump.Reachability;
22 import com.android.ahat.heapdump.Site;
23 import com.android.ahat.heapdump.Value;
24 import java.net.URI;
25 
26 /**
27  * Class for generating a DocString summary of an instance or value.
28  */
29 class Summarizer {
30 
31   // For string literals, we limit the number of characters we show to
32   // kMaxChars in case the string is really long.
33   private static int kMaxChars = 200;
34 
35   /**
36    * Creates a DocString representing a summary of the given instance.
37    */
summarize(AhatInstance inst)38   public static DocString summarize(AhatInstance inst) {
39     DocString formatted = new DocString();
40     if (inst == null) {
41       formatted.append("null");
42       return formatted;
43     }
44 
45     // Annotate new objects as new.
46     if (inst.getBaseline().isPlaceHolder()) {
47       formatted.append(DocString.added("new "));
48     }
49 
50     // Annotate deleted objects as deleted.
51     if (inst.isPlaceHolder()) {
52       formatted.append(DocString.removed("del "));
53     }
54 
55     // Annotate non-strongly reachable objects as such.
56     Reachability reachability = inst.getReachability();
57     if (reachability != Reachability.STRONG) {
58       formatted.append(reachability.toString() + " ");
59     }
60 
61     // Annotate roots as roots.
62     if (inst.isRoot()) {
63       formatted.append("root ");
64     }
65 
66     DocString linkText = DocString.text(inst.toString());
67     if (inst.isPlaceHolder()) {
68       // Don't make links to placeholder objects.
69       formatted.append(linkText);
70     } else {
71       URI objTarget = DocString.formattedUri("object?id=0x%x", inst.getId());
72       formatted.appendLink(objTarget, linkText);
73     }
74 
75     // Annotate Strings with their values.
76     String stringValue = inst.asString(kMaxChars);
77     if (stringValue != null) {
78       formatted.appendFormat(" \"%s", stringValue);
79       formatted.append(kMaxChars == stringValue.length() ? "..." : "\"");
80     }
81 
82     // Annotate Reference with its referent
83     AhatInstance referent = inst.getReferent();
84     if (referent != null) {
85       formatted.append(" for ");
86 
87       // It should not be possible for a referent to refer back to the
88       // reference object, even indirectly, so there shouldn't be any issues
89       // with infinite recursion here.
90       formatted.append(summarize(referent));
91     }
92 
93     // Annotate DexCache with its location.
94     String dexCacheLocation = inst.getDexCacheLocation(kMaxChars);
95     if (dexCacheLocation != null) {
96       formatted.appendFormat(" for %s", dexCacheLocation);
97       if (kMaxChars == dexCacheLocation.length()) {
98         formatted.append("...");
99       }
100     }
101 
102     // Annotate bitmaps with a thumbnail.
103     AhatInstance bitmap = inst.getAssociatedBitmapInstance();
104     if (bitmap != null) {
105       URI uri = DocString.formattedUri("bitmap?id=0x%x", bitmap.getId());
106       formatted.appendThumbnail(uri, "bitmap image");
107     }
108 
109     // Annotate $classOverhead arrays
110     AhatClassObj cls = inst.getAssociatedClassForOverhead();
111     if (cls != null) {
112       formatted.append(" overhead for ");
113       formatted.append(summarize(cls));
114     }
115 
116     // Annotate BinderProxy with its interface name.
117     String binderProxyInterface = inst.getBinderProxyInterfaceName();
118     if (binderProxyInterface != null) {
119       formatted.appendFormat(" for %s", binderProxyInterface);
120     }
121 
122     // Annotate Binder tokens with their descriptor
123     String binderTokenDescriptor = inst.getBinderTokenDescriptor();
124     if (binderTokenDescriptor != null) {
125       formatted.appendFormat(" binder token (%s)", binderTokenDescriptor);
126     }
127     // Annotate Binder services with their interface name.
128     String binderStubInterface = inst.getBinderStubInterfaceName();
129     if (binderStubInterface != null) {
130       formatted.appendFormat(" binder service (%s)", binderStubInterface);
131     }
132 
133     return formatted;
134   }
135 
136   /**
137    * Creates a DocString summarizing the given value.
138    */
summarize(Value value)139   public static DocString summarize(Value value) {
140     if (value == null) {
141       return DocString.text("null");
142     }
143     if (value.isAhatInstance()) {
144       return summarize(value.asAhatInstance());
145     }
146     return DocString.text(value.toString());
147   }
148 
149   /**
150    * Creates a DocString summarizing the given site.
151    */
summarize(Site site)152   public static DocString summarize(Site site) {
153     DocString text = DocString.text(site.getMethodName());
154     text.append(site.getSignature());
155     text.append(" - ");
156     text.append(site.getFilename());
157     if (site.getLineNumber() > 0) {
158       text.append(":").append(Integer.toString(site.getLineNumber()));
159     }
160     URI uri = DocString.formattedUri("site?id=%d", site.getId());
161     return DocString.link(uri, text);
162   }
163 }
164