This repository was archived by the owner on Aug 27, 2026. It is now read-only.
Commit 68062a7
[logcat-parse] Various semantic bugfixes (#500)
Context: dotnet/android#3706
A memory leak in an app is reported.
What Do We Do™? We enable GREF logging and use `logcat-parse`!
GREF logging is enabled via the `debug.mono.log` Android system
property, by having it include `gref`:
adb shell setprop debug.mono.log gref,timing,
When GREF logging is enabled, the app will write a `grefs.txt` file
into the `.__override__` directory. Launch the app, use it for
awhile, then extract the `grefs.txt` file:
adb shell run-as ActivityRecreateTest.ActivityRecreateTest cat files/.__override__/grefs.txt > grefs.txt
Once you have `grefs.txt`, use `logcat-parse.exe` to read it:
$ mono /Library/Frameworks/Xamarin.Android.framework/Versions/Current/lib/xamarin.android/xbuild/Xamarin/Android/logcat-parse.exe grefs.txt
# A different path is used on Windows, but it's still `logcat-parse.exe`
// `adb logcat` GREF parsing utility
//
// Use `Grefs.Parse(stream)` to parse a file containing `adb logcat` output.
// Grefs.AllocatedPeers contains all exposed Java.Lang.Object instances.
// Grefs.AlivePeers contains those still alive by the end of parsing.
var grefs = Grefs.Parse("grefs.txt");
That's when things get...weird:
csharp> grefs.GrefCount;
245
csharp> grefs.AlivePeers.Count();
18
There's a GREF count of 245, which matches what we can see within
`grefs.txt`, which has the following line toward the end:
+g+ grefc 245 gwrefc 0 obj-handle 0x79/I -> new-handle 0x2aa6/G from thread '(null)'(1)
...but there's only *18* peers? For 245 GREFs?
That does *not* make sense.
(It *is* possible to "alias" GREFs, but that's not very common in a
Xamarin.Android app, and the above would require ~14 GREFs per peer,
which is *very* unlikely.)
Turns Out™, `logcat-parse` has bugs!
![Bugs Everywhere][0]
~~ Initial Handle Reuse ~~
One of the problems was that the handle used when an instance entered
managed code could be *reused*:
+g+ grefc 11 gwrefc 0 obj-handle 0x71/L -> new-handle 0x266a/G from thread '(null)'(1)
...
+g+ grefc 15 gwrefc 0 obj-handle 0x71/I -> new-handle 0x26aa/G from thread '(null)'(1)
...
+g+ grefc 21 gwrefc 0 obj-handle 0x71/I -> new-handle 0x2786/G from thread '(null)'(1)
...
+g+ grefc 25 gwrefc 0 obj-handle 0x71/I -> new-handle 0x280a/G from thread '(null)'(1)
...
+g+ grefc 26 gwrefc 0 obj-handle 0x71/I -> new-handle 0x2816/G from thread '(null)'(1)
Note that `obj-handle 0x71` is present in *all* of these messages, but
they *all* refer to *different object instances*. However, because of
how `Grefs.GetPeerInfo()` and `PeerInfo.Handles` worked, they were all
associated with the same `PeerInfo` instance, because the *first*
`obj-handle 0x71` became the "owner" of all the *other*
`obj-handle 0x71` instances.
To address this, make two thanges to `Grefs` and `PeerInfo`:
1. Split up `PeerInfo.Handles` into `PeerInfo.Handles`, which
contains all *current* handles, and `PeerInfo.RemovedHandles`,
which contains all *previously seen but no longer valid*
JNI handles.
2. When creating a new `PeerInfo` instance, DO NOT use the
`obj-handle` value found in the `+g+` message. That is
*immediately* considered a `RemovedHandle` value.
These two changes prevent "accidental aliasing," in which
`logcat-parse` believes that two entirely unrelated JNI handles
actually belong to the same instance.
~~ Alive Peer Validation ~~
To help find related bugs, `GrefParseOptions` was overhauled so that
information about "Alive Peers" could be validated.
The semantics of `GrefParseOptions.CheckCounts` was altered so that
it's now a unique state; *just* specifying it won't do anything, it
needs to be combined with `GrefParseOptions.LogWarningOnMismatch` or
`GrefParseOptions.ThrowExceptionOnMismatch`, or continue using the
previously existing `GrefParseOptions.WarnOnCountMismatch` or
`GrefParseOptions.ThrowOnCountMismatch` options.
To these, we add:
partial enum GrefParseOptions {
CheckAlivePeers = 1 << 3,
WarnOnAlivePeerMismatch = CheckAlivePeers | LogWarningOnMismatch,
ThrowOnAlivePeerMismatch = CheckAlivePeers | ThrowExceptionOnMismatch,
}
You can enable Alive Peer validation by using the `Grefs.Parse()`
overload within `logcat-parse.exe`:
csharp> var grefs = Grefs.Parse ("grefs.txt", null, GrefParseOptions.ThrowOnAlivePeerMismatch);
which will cause an exception to be thrown when an "alive peer"
mismatch is encountered. This will happen when:
* A `Disposing handle` message is encountered, but no `PeerInfo` can
be found owning the specified handle.
* A `Finalizing.*handle` message is encountered, but no `PeerInfo`
can be found owning the specified handle.
* A ``handle HANDLE; key_handle HANDLE; Java Type: `...`; MCW Type: `...```
message is encountered, and (1) no `PeerInfo` can be found which
owns the specified handle, or (2) `PeerInfo.KeyHandle` has already
been set on the instance, and thus `logcat-parse` is erroneously
trying to *alter* an already-existing `PeerInfo` instance.
* A `-g-` message is encountered, and no `PeerInfo` can be found
which owns the specified GREF.
~~ Assorted Extras ~~
`JniHandleInfo` was updated so that a "full" handle can be used in
comparisons, e.g.:
grefs.AlivePeers.Count(p => p.Handles.Contains ("0x1234/G"));
Previously this wouldn't match, because the `0x1234/G` would be
compared against `0x1234` (no `/G`) in `JniHandleInfo.Handle`.
`PeerInfo.Handles` is now an `IList<JniHandleInfo>`, instead of an
`ISet<JniHandleInfo>`, so that we can now reason about *time*: earlier
entries will be created earlier in time, and the order of
`PeerInfo.Handles` and `PeerInfo.RemovedHandles` is the order
encountered within the log file.
~~ Result ~~
With the above changes, processing `grefs.txt` makes more sense:
$ mono /Library/Frameworks/Xamarin.Android.framework/Versions/Current/lib/xamarin.android/xbuild/Xamarin/Android/logcat-parse.exe grefs.txt
...
csharp> grefs.GrefCount;
245
csharp> grefs.AlivePeers.Count();
245
This in turn allows us to get appropriate type counts:
csharp> grefs.GetAliveJniTypeCounts();
{{ "", 14 },
{ "com/android/internal/os/RuntimeInit$KillApplicationHandler", 1 },
{ "android/runtime/UncaughtExceptionHandler", 1 },
{ "md5327d1d288bcaf8c0cc06d09d4b2f8e3a/MainActivity", 45 },
{ "android/support/v7/widget/Toolbar", 45 },
{ "android/support/design/widget/FloatingActionButton", 45 },
{ "android/support/v7/view/menu/MenuBuilder", 45 },
{ "android/support/v7/view/SupportMenuInflater", 45 },
{ "mono/android/view/View_OnClickListenerImplementor", 3 },
{ "android/os/Bundle", 1 }}
For this run, there are 45 `MainActivity` instances, along with
instances which `MainActivity` references, such as `ToolBar`,
`FloatingActionButton`, `MenuBuilder`, etc.
[0]: https://media.makeameme.org/created/bugs-bugs-everywhere-kyn0a3.jpg1 parent 7a309c4 commit 68062a7
4 files changed
Lines changed: 142 additions & 64 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
10 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
11 | 19 | | |
12 | 20 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
120 | | - | |
| 120 | + | |
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
| |||
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
129 | | - | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
130 | 134 | | |
131 | 135 | | |
132 | 136 | | |
133 | | - | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
134 | 142 | | |
135 | 143 | | |
136 | 144 | | |
137 | | - | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
138 | 154 | | |
139 | 155 | | |
140 | 156 | | |
141 | 157 | | |
142 | 158 | | |
143 | 159 | | |
144 | | - | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
145 | 164 | | |
146 | | - | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
147 | 170 | | |
148 | 171 | | |
149 | 172 | | |
| |||
155 | 178 | | |
156 | 179 | | |
157 | 180 | | |
158 | | - | |
| 181 | + | |
159 | 182 | | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
164 | 193 | | |
165 | 194 | | |
166 | 195 | | |
| |||
188 | 217 | | |
189 | 218 | | |
190 | 219 | | |
191 | | - | |
192 | | - | |
| 220 | + | |
193 | 221 | | |
194 | 222 | | |
195 | 223 | | |
196 | 224 | | |
197 | 225 | | |
198 | | - | |
| 226 | + | |
199 | 227 | | |
200 | 228 | | |
201 | 229 | | |
202 | | - | |
| 230 | + | |
203 | 231 | | |
204 | 232 | | |
205 | | - | |
206 | | - | |
207 | | - | |
| 233 | + | |
208 | 234 | | |
209 | 235 | | |
210 | 236 | | |
211 | | - | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
212 | 256 | | |
213 | 257 | | |
214 | 258 | | |
| |||
218 | 262 | | |
219 | 263 | | |
220 | 264 | | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
221 | 269 | | |
222 | 270 | | |
223 | 271 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
101 | 103 | | |
102 | 104 | | |
103 | 105 | | |
104 | 106 | | |
105 | | - | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
106 | 110 | | |
107 | 111 | | |
108 | 112 | | |
109 | 113 | | |
110 | | - | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
111 | 117 | | |
112 | 118 | | |
113 | 119 | | |
114 | 120 | | |
115 | | - | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
116 | 124 | | |
117 | 125 | | |
118 | 126 | | |
| |||
154 | 162 | | |
155 | 163 | | |
156 | 164 | | |
157 | | - | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
158 | 168 | | |
159 | 169 | | |
160 | 170 | | |
| |||
167 | 177 | | |
168 | 178 | | |
169 | 179 | | |
170 | | - | |
| 180 | + | |
171 | 181 | | |
| 182 | + | |
172 | 183 | | |
173 | 184 | | |
174 | 185 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
34 | 36 | | |
35 | 37 | | |
36 | 38 | | |
| |||
60 | 62 | | |
61 | 63 | | |
62 | 64 | | |
63 | | - | |
64 | | - | |
65 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
66 | 69 | | |
67 | 70 | | |
68 | 71 | | |
| |||
95 | 98 | | |
96 | 99 | | |
97 | 100 | | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
103 | 107 | | |
104 | 108 | | |
105 | 109 | | |
| |||
130 | 134 | | |
131 | 135 | | |
132 | 136 | | |
133 | | - | |
134 | | - | |
135 | | - | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
136 | 141 | | |
137 | 142 | | |
138 | 143 | | |
| |||
185 | 190 | | |
186 | 191 | | |
187 | 192 | | |
188 | | - | |
189 | | - | |
| 193 | + | |
190 | 194 | | |
191 | 195 | | |
| 196 | + | |
| 197 | + | |
192 | 198 | | |
193 | 199 | | |
194 | 200 | | |
| |||
220 | 226 | | |
221 | 227 | | |
222 | 228 | | |
223 | | - | |
224 | | - | |
225 | | - | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
226 | 233 | | |
227 | 234 | | |
228 | 235 | | |
| |||
250 | 257 | | |
251 | 258 | | |
252 | 259 | | |
253 | | - | |
254 | | - | |
255 | | - | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
256 | 264 | | |
257 | 265 | | |
258 | 266 | | |
| |||
289 | 297 | | |
290 | 298 | | |
291 | 299 | | |
292 | | - | |
293 | | - | |
294 | 300 | | |
| 301 | + | |
| 302 | + | |
295 | 303 | | |
296 | 304 | | |
297 | 305 | | |
| |||
356 | 364 | | |
357 | 365 | | |
358 | 366 | | |
359 | | - | |
360 | | - | |
361 | | - | |
362 | | - | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
363 | 372 | | |
364 | 373 | | |
365 | 374 | | |
| |||
420 | 429 | | |
421 | 430 | | |
422 | 431 | | |
423 | | - | |
424 | | - | |
425 | | - | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
426 | 436 | | |
427 | 437 | | |
428 | 438 | | |
| |||
439 | 449 | | |
440 | 450 | | |
441 | 451 | | |
442 | | - | |
443 | | - | |
444 | | - | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
445 | 456 | | |
446 | 457 | | |
447 | 458 | | |
| |||
0 commit comments