GT reports available for data collection

Reports collect data about a running image, to analyse problems later: slowdowns, stuck processes, garbage collections, memory use. Every report carries the image data (GtReportImageData Object << #GtReportImageData slots: { #gtVersion . #gtReleaseTimestamp . #gtVmVersion . #pharoVmVersion . #pharoImageVersion }; package: 'GToolkit-Utility-Basic' ): the GT, GT VM, Pharo VM and Pharo image versions, and nothing that identifies the user or the machine. How to analyse stored reports is on Analysing stored GT reports; how the infrastructure works and how to add a report is on Report infrastructure for collecting data in GT.

Each section below has two or three snippets: the first creates the report (inspect it), the next stores it (#selector storeIn: aStore ^ aStore store: self ). A report can also be stored from the inspector with its Save locally action (#selector gtSaveLocallyActionFor: anAction <gtAction> ^ anAction button icon: BrGlamorousVectorIcons savetodisk; tooltip: 'Save the report locally'; action: [ :aButton | aButton phlow spawnObject: (self storeIn: GtReportsLocalStore default) ] ), and a recorder can record and store in one go (#selector gtRecordAndSaveLocallyActionFor: anAction <gtAction> ^ anAction button icon: BrGlamorousVectorIcons savetodisk; label: 'Record'; tooltip: 'Record in the background and save locally'; priority: 10; action: [ :aButton | aButton phlow spawnObject: self recordLocallyInBackground ] ).

Where a report goes depends on the store:

- GtReportsLocalStore GtReportsStore << #GtReportsLocalStore slots: { #rootFolder }; package: 'GToolkit-Utility-SpaceTally' #selector default ^ self rootFolder: FileLocator imageDirectory / 'gt-reports' : the gt-reports folder next to the image

- GtReportsLocalStore GtReportsStore << #GtReportsLocalStore slots: { #rootFolder }; package: 'GToolkit-Utility-SpaceTally' #selector rootFolder: aFileReference ^ self new setRootFolder: aFileReference : another folder, e.g. a memory file system for experiments

- GtReportsCompositeStore GtReportsStore << #GtReportsCompositeStore slots: { #stores }; package: 'GToolkit-Utility-SpaceTally' #selector withAll: aCollectionOfStores ^ self new setStores: aCollectionOfStores asArray : several stores at once, with the same folder name in each

- GtNoReportsStore GtReportsStore << #GtNoReportsStore slots: {}; package: 'GToolkit-Utility-SpaceTally' : keeps nothing, to record a report only to inspect it

A snapshot of all processes in the image, with their stacks (GtProcessesReport Object << #GtProcessesReport slots: { #processReports . #imageData . #reportUid }; package: 'GToolkit-Utility-SpaceTally' , recorded by #selector recordNewReport ^ self new recordImageData; recordReportStatistics; yourself ).

processesReport := GtProcessesReport recordNewReport.
  
processesReport storeIn: GtReportsLocalStore default.
  

Profiles the whole image for 30 seconds (#selector initialize super initialize. duration := 30 seconds. pollPeriodMilliseconds := MessageTally defaultPollPeriod ) in a background process, so the UI stays usable and what you do in it is profiled (GtMessageTallyReport Object << #GtMessageTallyReport slots: { #textualContent . #imageData . #reportUid . #startTime . #duration . #pollPeriodMilliseconds . #numberOfSamples . #measuredMilliseconds . #vmParametersDiff . #messageTally }; package: 'GToolkit-Utility-SpaceTally' , GtMessageTallyReportRecorder GtReportRecorder << #GtMessageTallyReportRecorder slots: { #duration . #pollPeriodMilliseconds }; package: 'GToolkit-Utility-SpaceTally' ). Inspect the recording (GtReportRecording Object << #GtReportRecording slots: { #recorder . #store . #announcer . #process . #startedAt . #report . #entry . #error }; package: 'GToolkit-Utility-SpaceTally' ): the report shows up in it when the recording ends. It is stored as the text report plus a JSON file with the other data.

messageTallyRecording := (GtMessageTallyReportRecorder current
	duration: 30 seconds)
		recordInBackgroundWithoutStoring.
  
messageTallyRecording report storeIn: GtReportsLocalStore default.
  

The full garbage collections recorded since the recorder was activated, with how the VM parameters changed (GtGarbageCollectsReport Object << #GtGarbageCollectsReport slots: { #events . #overallParametersDiffReport . #imageData . #reportUid }; package: 'GToolkit-Utility-GarbageCollectAnalysis' ). The recorder (GtGarbageCollectsRecorder Object << #GtGarbageCollectsRecorder slots: { #events . #isEnabled . #initialVmParametersReport }; package: 'GToolkit-Utility-GarbageCollectAnalysis' ) only sees garbage collections while it is active: activate it first (#selector activate self install. self startRecording. ), deactivate it when done (#selector deactivate self uninstall. self disable. ).

GtGarbageCollectsRecorder defaultInstance activate.
  
garbageCollectsReport := GtGarbageCollectsRecorder defaultInstance createReport.
  
garbageCollectsReport storeIn: GtReportsLocalStore default.
  

The memory taken by the instances and the code of every class (GtSpaceTally Object << #GtSpaceTally slots: { #label . #results }; package: 'GToolkit-Utility-SpaceTally' ), together with the VM memory statistics (GtSpaceTallyReport Object << #GtSpaceTallyReport slots: { #imageData . #reportUid . #timestamp . #spaceTally . #vmStatisticsReport }; package: 'GToolkit-Utility-SpaceTally' , GtSpaceTallyReportRecorder GtReportRecorder << #GtSpaceTallyReportRecorder slots: {}; package: 'GToolkit-Utility-SpaceTally' ). Recording takes a few seconds in a background process, and the image freezes while the VM walks the heap. Inspect the recording: the report shows up in it when the recording ends. The report is stored as gzipped STON, about 3 MB for a large image; storing takes about 40 seconds, and reading it back about a minute. Read a stored file back with GtSpaceTallyReport Object << #GtSpaceTallyReport slots: { #imageData . #reportUid . #timestamp . #spaceTally . #vmStatisticsReport }; package: 'GToolkit-Utility-SpaceTally' #selector fromGzippedSton: aByteArray ^ STON fromString: (GZipReadStream on: aByteArray) upToEnd utf8Decoded .

spaceTallyRecording := GtSpaceTallyReportRecorder current
	recordInBackgroundWithoutStoring.
  
spaceTallyRecording report storeIn: GtReportsLocalStore default.
  

A recorder can also record in the background and store the report itself (#selector recordLocallyInBackground ^ self recordingLocally start , or #selector recordInBackgroundInto: aStore ^ (self recordingInto: aStore) start for another store). The recording shows the report, the stored files (#selector gtStoredFilesFor: aView <gtView> entry ifNil: [ ^ aView empty ]. ^ aView forward title: 'Stored files'; priority: 10; object: [ entry ]; view: #gtFilesFor:; actionButtonIcon: BrGlamorousVectorIcons inspect tooltip: 'Inspect the stored entry' action: [ :aButton | aButton phlow spawnObject: entry ] ) and the written files themselves (#selector gtFolderFor: aView <gtView> entry ifNil: [ ^ aView empty ]. ^ aView forward title: 'Folder'; priority: 11; object: [ entry ]; view: #gtFolderFor: ) once it has ended.

processesRecording := GtProcessesReportRecorder current recordLocallyInBackground.
  

Each stored report is one folder <type>/<yyyy-mm-dd>/<storage id>/ (#selector folderFor: aReportType storageId: aStorageId machineName: aMachineName ^ (self folderForType: aReportType) / (self dateOf: aStorageId) / aStorageId in GtReportsStore Object << #GtReportsStore slots: {}; package: 'GToolkit-Utility-SpaceTally' ). The storage id is the UTC time plus a short random part, for example 20261108T093015Z-c8538f0a (#selector newStorageId ^ self utcTimestampString , '-' , (UUID new asString first: 8) ); the date folder is its UTC day. Files are named after the report type (#selector reportType ^ 'processes' ), e.g. message-tally.txt and message-tally.json, or after their role when a report has several files of the same kind (picture-1.png, picture-2.png, written with #selector writeBytes: aByteArray named: aName extension: anExtension ^ self record: (GtReportsStoreDataFile destination: folder / (aName , '.' , anExtension)) writing: [ :aDestination | store writeBytes: aByteArray to: aDestination ] ).

meta.json (GtReportMetadata Object << #GtReportMetadata slots: { #formatVersion . #reportType . #reportClassName . #reportAlias . #reportUid . #storageId . #storedAt . #machineName . #imageData . #files }; package: 'GToolkit-Utility-SpaceTally' ) is written last, so a folder without it is incomplete. It holds three names: the report type (#selector reportType ^ reportType : the kind of data, used for the folders and for grouping), the report class (#selector reportClassName ^ reportClassName : how to read the report now) and the report alias (#selector reportAlias ^ reportAlias : how to read it after a class rename). It holds two ids: the report uid (#selector reportUid ^ reportUid ), given to the report when it is created (#selector initialize super initialize. processReports := #(). imageData := GtReportImageData new. reportUid := UUID new ), and the storage id of this copy (#selector storageId ^ storageId ). Storing the same report again gives a new storage id but the same report uid, so copies can be recognised. meta.json also holds the UTC time, the image data, the data files with their sizes (#selector files ^ files , GtReportFileDescription Object << #GtReportFileDescription slots: { #name . #numberOfBytes }; package: 'GToolkit-Utility-SpaceTally' ), the format version (#selector currentFormatVersion ^ 1 ) and a machine name, empty in plain GT (#selector machineName ^ '' , #selector machineNameFor: aReport ^ (aReport imageData machineName ifEmpty: [ self machineName ]) asLowercase ).