115
})
116
}
117
>
sortGroups(report.unexpectedObjects)
report.go
118
>
sortGroups(report.expectedObjects)
119
>
slices.Sort(report.unmatchedExpected)
120
>
slices.Sort(report.unmatchedPrunes)
121
>
return report
122
}
123
124
>
func (r report) failures() error {
report.go
125
>
var failures []error
126
>
if len(r.unexpectedObjects) > 0 {
127
failures = append(failures, errors.New("unexpected retained objects"))
128
}
129
>
if len(r.unmatchedExpected) > 0 {
report.go
130
failures = append(failures, errors.New("stale expected patterns"))
131
}
132
>
if len(r.unmatchedPrunes) > 0 {
report.go
133
failures = append(failures, errors.New("stale prunes"))
134
}
135
>
return errors.Join(failures...)
report.go
136
}
137
138
>
func (r report) totals() [3]int {
report.go
139
>
return [3]int{
140
>
r.totalRetainedObjects,
141
>
r.unexpectedRetainedObjects,
142
>
len(r.unmatchedExpected) + len(r.unmatchedPrunes),
143
>
}
144
>
}
145
146
>
func (r report) string() string {
report.go
147
>
var out strings.Builder
148
>
r.writeSummary(&out)
149
>
150
>
writeGroups := func(title string, groups []objectGroup) {
151
>
fmt.Fprintf(&out, "%s:\n", title)
152
>
if len(groups) == 0 {
153
>
out.WriteString(" none\n")
154
>
return
155
>
}
156
>
for _, group := range groups {
157
>
fmt.Fprintf(&out, " %s: %s\n", group.counts(), group.name())
158
>
}
159
}
161
>
writeGroups("unexpected retained objects", r.unexpectedObjects)
162
>
out.WriteByte('\n')
163
>
writeGroups("expected retained objects", r.expectedObjects)
164
>
165
>
if len(r.unmatchedExpected) > 0 {
166
out.WriteString("\nstale expected patterns:\n")
167
}
168
>
for _, pattern := range r.unmatchedExpected {
report.go
169
fmt.Fprintf(&out, " %s\n", pattern)
170
}
171
172
>
if len(r.unmatchedPrunes) > 0 {
report.go
173
out.WriteString("\nstale prunes:\n")
174
}
175
>
for _, pattern := range r.unmatchedPrunes {
report.go
176
fmt.Fprintf(&out, " %s\n", pattern)
177
}
178
>
return strings.TrimSuffix(out.String(), "\n")
report.go
179
}
180
181
>
func (r report) writeSummary(out *strings.Builder) {
report.go
182
>
out.WriteString("object leak report\n\n")
183
>
fmt.Fprintf(out, "tracked root objects: %d\n", r.trackedRoots)
184
>
fmt.Fprintf(
185
>
out,
186
>
"retained paths: %d total, %d expected, %d unexpected\n",
187
>
r.totalRetainedPaths,
188
>
r.expectedRetainedPaths,
189
>
r.unexpectedRetainedPaths,
190
>
)
191
>
fmt.Fprintf(
192
>
out,
193
>
"retained objects: %d total, %d expected, %d unexpected\n",
194
>
r.totalRetainedObjects,
195
>
r.expectedRetainedObjects,
196
>
r.unexpectedRetainedObjects,
197
>
)
198
>
}
199
200
>
func (g objectGroup) name() string {
report.go
201
>
if g.path == "" {
202
>
return g.typeName
203
>
}
204
>
return fmt.Sprintf("%s (%s)", g.path, g.typeName)
205
}
206
207
>
func (g objectGroup) objectCount() int {
report.go
208
>
return len(g.addresses)
209
>
}
210
211
>
func (g objectGroup) counts() string {
report.go
212
>
objects := g.objectCount()
213
>
if g.paths == objects {
214
>
return formatCount(objects, "object")
215
>
}
216
>
return fmt.Sprintf("%s, %s", formatCount(g.paths, "path"), formatCount(objects, "object"))
217
}
218
219
>
func formatCount(count int, label string) string {
report.go
220
>
if count == 1 {
221
>
return fmt.Sprintf("1 %s", label)
222
>
}
223
>
return fmt.Sprintf("%d %ss", count, label)
224
}