Skip to content

Commit 8fa8f50

Browse files
committed
Add garbage collection recording and analysis
1 parent ca2b990 commit 8fa8f50

9 files changed

+631
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
Class {
2+
#name : #GtBareRecordedClass,
3+
#superclass : #Object,
4+
#instVars : [
5+
'targetClassName',
6+
'isMeta'
7+
],
8+
#category : #'GToolkit-Utility-GarbageCollectAnalysis'
9+
}
10+
11+
{ #category : #'instance creation' }
12+
GtBareRecordedClass class >> forClass: aClass [
13+
^ self new
14+
initializeForClass: aClass
15+
]
16+
17+
{ #category : #comparing }
18+
GtBareRecordedClass >> = anObject [
19+
20+
"Answer whether the receiver and anObject represent the same object."
21+
22+
self == anObject ifTrue: [ ^ true ].
23+
self class = anObject class ifFalse: [ ^ false ].
24+
^ targetClassName = anObject targetClassName and: [
25+
isMeta = anObject isMeta ]
26+
]
27+
28+
{ #category : #comparing }
29+
GtBareRecordedClass >> hash [
30+
^ self targetClassName hash
31+
bitXor: self isMeta hash
32+
]
33+
34+
{ #category : #initialization }
35+
GtBareRecordedClass >> initializeForClass: aClass [
36+
targetClassName := aClass instanceSide name.
37+
isMeta := aClass isMeta
38+
]
39+
40+
{ #category : #accessing }
41+
GtBareRecordedClass >> isMeta [
42+
43+
^ isMeta
44+
]
45+
46+
{ #category : #accessing }
47+
GtBareRecordedClass >> isMeta: aBoolean [
48+
49+
isMeta := aBoolean
50+
]
51+
52+
{ #category : #printing }
53+
GtBareRecordedClass >> printOn: aStream [
54+
super printOn: aStream.
55+
56+
aStream parenthesize: [
57+
aStream << self targetClassNameDescription ]
58+
]
59+
60+
{ #category : #accessing }
61+
GtBareRecordedClass >> targetClassName [
62+
^ targetClassName
63+
]
64+
65+
{ #category : #accessing }
66+
GtBareRecordedClass >> targetClassName: aClassName [
67+
targetClassName := aClassName
68+
]
69+
70+
{ #category : #printing }
71+
GtBareRecordedClass >> targetClassNameDescription [
72+
^ self isMeta
73+
ifTrue: [ self targetClassName asString ]
74+
ifFalse: [ self targetClassName asString, ' class' ]
75+
]
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
Class {
2+
#name : #GtBareRecordedStackFrameCall,
3+
#superclass : #Object,
4+
#instVars : [
5+
'selector',
6+
'methodClassRecord',
7+
'receiverClassRecord',
8+
'programCounter'
9+
],
10+
#category : #'GToolkit-Utility-GarbageCollectAnalysis'
11+
}
12+
13+
{ #category : #'instance creation' }
14+
GtBareRecordedStackFrameCall class >> forContext: aContext [
15+
^ self new
16+
initializeForContext: aContext
17+
]
18+
19+
{ #category : #printing }
20+
GtBareRecordedStackFrameCall >> descriptionOn: aStream [
21+
self methodDescriptionOn: aStream.
22+
23+
aStream nextPutAll: '; pc: '.
24+
aStream print: self programCounter.
25+
]
26+
27+
{ #category : #views }
28+
GtBareRecordedStackFrameCall >> gtViewTargetMethodSourceFor: aView [
29+
<gtView>
30+
^ aView forward
31+
title: 'Source';
32+
priority: 10;
33+
object: [ self targetMethodReference ];
34+
view: #gtSourceFor:
35+
]
36+
37+
{ #category : #testing }
38+
GtBareRecordedStackFrameCall >> hasDifferentReceiverClass [
39+
^ methodClassRecord ~= receiverClassRecord
40+
]
41+
42+
{ #category : #accessing }
43+
GtBareRecordedStackFrameCall >> initializeForContext: aContext [
44+
selector := aContext selector.
45+
methodClassRecord := GtBareRecordedClass forClass: aContext methodClass.
46+
receiverClassRecord := GtBareRecordedClass forClass: aContext receiver class.
47+
programCounter := aContext pc.
48+
]
49+
50+
{ #category : #accessing }
51+
GtBareRecordedStackFrameCall >> methodClassRecord [
52+
^ methodClassRecord
53+
]
54+
55+
{ #category : #accessing }
56+
GtBareRecordedStackFrameCall >> methodClassRecord: aRecordedClass [
57+
methodClassRecord := aRecordedClass
58+
]
59+
60+
{ #category : #printing }
61+
GtBareRecordedStackFrameCall >> methodDescription [
62+
^ String streamContents: [ :aStream |
63+
self methodDescriptionOn: aStream ]
64+
]
65+
66+
{ #category : #printing }
67+
GtBareRecordedStackFrameCall >> methodDescriptionOn: aStream [
68+
aStream nextPutAll: self receiverClassRecord targetClassName.
69+
self hasDifferentReceiverClass ifTrue: [
70+
aStream nextPut: $(.
71+
aStream nextPutAll: self methodClassRecord targetClassName.
72+
aStream nextPut: $) ].
73+
aStream nextPutAll: '>>'.
74+
aStream nextPutAll: self selector.
75+
]
76+
77+
{ #category : #printing }
78+
GtBareRecordedStackFrameCall >> printOn: aStream [
79+
super printOn: aStream.
80+
81+
aStream parenthesize: [
82+
self descriptionOn: aStream ]
83+
]
84+
85+
{ #category : #accessing }
86+
GtBareRecordedStackFrameCall >> programCounter [
87+
^ programCounter
88+
]
89+
90+
{ #category : #accessing }
91+
GtBareRecordedStackFrameCall >> programCounter: aProgramCounter [
92+
programCounter := aProgramCounter
93+
]
94+
95+
{ #category : #accessing }
96+
GtBareRecordedStackFrameCall >> receiverClassRecord [
97+
^ receiverClassRecord
98+
]
99+
100+
{ #category : #accessing }
101+
GtBareRecordedStackFrameCall >> receiverClassRecord: aRecordedClass [
102+
receiverClassRecord := aRecordedClass
103+
]
104+
105+
{ #category : #accessing }
106+
GtBareRecordedStackFrameCall >> selector [
107+
^ selector
108+
]
109+
110+
{ #category : #accessing }
111+
GtBareRecordedStackFrameCall >> selector: aSelector [
112+
selector := aSelector
113+
]
114+
115+
{ #category : #accessing }
116+
GtBareRecordedStackFrameCall >> targetMethodReference [
117+
^ RGMethodDefinition new
118+
name: self selector;
119+
parentName: self methodClassRecord targetClassName;
120+
isMeta: self methodClassRecord isMeta;
121+
asActive
122+
]
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
Class {
2+
#name : #GtGarbageCollectEvent,
3+
#superclass : #Object,
4+
#instVars : [
5+
'largestFreeChunkSize',
6+
'parametersDiffReport',
7+
'callerStackTraceRecords',
8+
'requestedSize'
9+
],
10+
#category : #'GToolkit-Utility-GarbageCollectAnalysis'
11+
}
12+
13+
{ #category : #accessing }
14+
GtGarbageCollectEvent >> duration [
15+
^self endVmParameters timestamp - self startVmParameters timestamp
16+
]
17+
18+
{ #category : #accessing }
19+
GtGarbageCollectEvent >> endVmParameters [
20+
^ self parametersDiffReport targetReport
21+
]
22+
23+
{ #category : #recording }
24+
GtGarbageCollectEvent >> extractRequestedSizeFrom: aStack [
25+
aStack
26+
detect: [ :aContext |
27+
aContext selector = #handleFailingBasicNew: and: [
28+
aContext methodClass name = #Behavior ] ]
29+
ifFound: [ :aContext |
30+
| var |
31+
var := aContext lookupVar: #sizeRequested.
32+
var isLocalVariable ifTrue: [
33+
requestedSize := var readInContext: aContext ] ]
34+
]
35+
36+
{ #category : #views }
37+
GtGarbageCollectEvent >> gtChangedParametersViewFor: aView [
38+
<gtView>
39+
^ aView forward
40+
title: 'Changed Parameters';
41+
priority: 10;
42+
object: [ self parametersDiffReport ];
43+
view: #gtViewChangedParametersFor:
44+
]
45+
46+
{ #category : #views }
47+
GtGarbageCollectEvent >> gtEndVmParametersFor: aView [
48+
<gtView>
49+
^ aView forward
50+
title: 'End Parameters';
51+
priority: 16.2;
52+
object: [ self endVmParameters ];
53+
view: #gtViewVmParametersFor:
54+
]
55+
56+
{ #category : #views }
57+
GtGarbageCollectEvent >> gtStartVmParametersFor: aView [
58+
<gtView>
59+
^ aView forward
60+
title: 'Start Parameters';
61+
priority: 16.1;
62+
object: [ self startVmParameters ];
63+
view: #gtViewVmParametersFor:
64+
]
65+
66+
{ #category : #views }
67+
GtGarbageCollectEvent >> gtViewCallerStackFor: aView [
68+
<gtView>
69+
callerStackTraceRecords ifNil: [ ^ aView empty ].
70+
^ aView columnedList
71+
title: 'Caller Stack Record';
72+
priority: 15;
73+
items: [ callerStackTraceRecords ];
74+
column: 'Method' text: [ :each | each methodDescription ];
75+
column: 'Program Counter' text: [ :each | each programCounter ] width: 120
76+
]
77+
78+
{ #category : #accessing }
79+
GtGarbageCollectEvent >> largestFreeChunkSize [
80+
^ largestFreeChunkSize
81+
]
82+
83+
{ #category : #accessing }
84+
GtGarbageCollectEvent >> largestFreeChunkSize: anInteger [
85+
largestFreeChunkSize := anInteger
86+
]
87+
88+
{ #category : #accessing }
89+
GtGarbageCollectEvent >> largestFreeChunkSizeDescription [
90+
^ self largestFreeChunkSize humanReadableSISizeString
91+
]
92+
93+
{ #category : #recording }
94+
GtGarbageCollectEvent >> numberOfContextsToExtract [
95+
^ 30
96+
]
97+
98+
{ #category : #accessing }
99+
GtGarbageCollectEvent >> parametersDiffReport [
100+
^ parametersDiffReport
101+
]
102+
103+
{ #category : #printing }
104+
GtGarbageCollectEvent >> printOn: aStream [
105+
super printOn: aStream .
106+
aStream parenthesize: [
107+
aStream
108+
<< self startTime asTime print24;
109+
<< '; ';
110+
<< self duration gtShortPrintString;
111+
<< '; freed: ';
112+
<< self reducedOldSpaceSizeDescription;
113+
<< ' [largest chunk: ';
114+
<< self largestFreeChunkSizeDescription;
115+
<< ']' ]
116+
]
117+
118+
{ #category : #recording }
119+
GtGarbageCollectEvent >> recordCallerStackTrace [
120+
| callerStackTrace |
121+
callerStackTrace := thisContext stackOfSize: self numberOfContextsToExtract.
122+
123+
callerStackTraceRecords := callerStackTrace
124+
collect: [ :each | GtBareRecordedStackFrameCall forContext: each ].
125+
126+
self extractRequestedSizeFrom: callerStackTrace
127+
]
128+
129+
{ #category : #recording }
130+
GtGarbageCollectEvent >> recordEndReport [
131+
parametersDiffReport recordTargetReport.
132+
self recordCallerStackTrace.
133+
]
134+
135+
{ #category : #recording }
136+
GtGarbageCollectEvent >> recordStartReport [
137+
parametersDiffReport := GtVmAllParametersDiffReport new.
138+
parametersDiffReport recordSourceReport
139+
]
140+
141+
{ #category : #accessing }
142+
GtGarbageCollectEvent >> reducedOldSpaceSizeDescription [
143+
^ self parametersDiffReport reducedOldSpaceSize
144+
humanReadableSISizeString
145+
]
146+
147+
{ #category : #accessing }
148+
GtGarbageCollectEvent >> requestedSizeDescription [
149+
^ requestedSize
150+
ifNil: [ '-' ]
151+
ifNotNil: [ :anInteger | anInteger humanReadableSISizeString ]
152+
]
153+
154+
{ #category : #accessing }
155+
GtGarbageCollectEvent >> sourceDescription [
156+
^ requestedSize
157+
ifNil: ['<unknowm>']
158+
ifNotNil: [ :anInteger |
159+
'BasicNew [', self requestedSizeDescription, ']' ]
160+
]
161+
162+
{ #category : #accessing }
163+
GtGarbageCollectEvent >> startTime [
164+
^ self startVmParameters timestamp
165+
]
166+
167+
{ #category : #accessing }
168+
GtGarbageCollectEvent >> startVmParameters [
169+
^ self parametersDiffReport sourceReport
170+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Class {
2+
#name : #GtGarbageCollectNullEventRecorder,
3+
#superclass : #Object,
4+
#category : #'GToolkit-Utility-GarbageCollectAnalysis'
5+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Class {
2+
#name : #GtGarbageCollectSingleEventRecorder,
3+
#superclass : #Object,
4+
#instVars : [
5+
'newEvent'
6+
],
7+
#category : #'GToolkit-Utility-GarbageCollectAnalysis'
8+
}
9+
10+
{ #category : #recording }
11+
GtGarbageCollectSingleEventRecorder >> recordGarbageCollectEndWithResult: aResult [
12+
newEvent largestFreeChunkSize: aResult.
13+
newEvent recordEndReport.
14+
]
15+
16+
{ #category : #recording }
17+
GtGarbageCollectSingleEventRecorder >> recordGarbageCollectStart [
18+
newEvent := GtGarbageCollectEvent new.
19+
newEvent recordStartReport.
20+
]

0 commit comments

Comments
 (0)