понедельник, 6 июня 2016 г.

ObjectStatistics. Simple objects analysis tool

I am glad to announce ObjectStatistics tool to analyse set of objects by computing different kind of metrics and look at them from different angles. It implements simplistic OLAP Cube approach for data analysis but in objects space.
Imaging that we have collection of message sends and we want to know number of message sends in dimension of receiver, receiver class and message selector. We have different angles to look at this data: from receiver class to selector and receiver or from selector to receiver class and receiver or any other combination. 
We also could analyze different kind of metrics which could be computed on given objects. It could be number of unique receivers, execution time, executed lines of code, etc.
This package implements computation of object statistics over declared metrics and dimensions space.
For example code profilers could be easily implemented by this tool. Following code will produce statistics for printing SmallInteger 3:
ObjectStatistics>>exampleFlatProfiler
 | stat prev |
  stat := ObjectStatistics new.
  stat countAllAs: 'sends'.
  stat countDifferent: [:context | context receiver ] as: 'receivers'. 

  stat 
    dimension: [ :context | context receiver class ] 
    named: 'classes'; with: [ 
        stat dimension: [ :context | context selector ] 
            named: 'msgs' ];
    dimension: [ :context | context selector ] 
    named: 'msgs'; with: [ 
        stat dimension: [ :context | context receiver class ] 
            named: 'classes' ];
    dimension: [ :context | context receiver class -> context selector ] 
    named: 'leaves'; with: [ 
        stat dimension: [ :context | context sender method ] 
            named: 'sender' ].
 
  prev := nil.
  thisContext sender 
    runSimulated: [3 printString] 
    contextAtEachStep: [:current |
 current ~~ prev & (current sender == prev ) ifTrue: [
  stat accumulate: current].
 prev := current].
   
  ^stat inspect
ObjectStatistics provides GTTools integration which shows computed objects in suitable way:

Usually profilers show execution tree to analyse how many message sends was happen per method call or how many time was spent on each method call. To achieve this ObjectStatistics implements special recursive dimension which produce metrics per children::
ObjectStatistics>>exampleTreeProfiler
| stat prev |
stat := ObjectStatistics new.
stat countAllAs: 'sends'.
stat countDifferent: [:context | context receiver ] as: 'receivers'. 
  
stat 
    dimension: [ :context | context receiver class -> context selector] 
    named: 'calls' recursionUpTo: [:context | context sender].
 
prev := nil.
thisContext sender 
    runSimulated: [3 printString] 
    contextAtEachStep: [:current |
 current ~~ prev & (current sender == prev ) ifTrue: [
  stat accumulate: current].
 prev := current].
   
^stat inspect
And inspector will show regular profiler tree:

(here we also see stack frames which belongs to tool which ran example). 
At the end lets look at methods of some package in multidimensional space:
ObjectStatistics>>exampleMethods
| stat |
  stat := ObjectStatistics new.
  stat countAllAs: 'methods'.
  stat countFunction: [:m | m linesOfCode ] as: 'lines of code'. 
 
  stat 
    dimension: [ :m | m methodClass package name ] 
    named: 'pckgs'; with: [ 
 stat dimension: [ :m | m methodClass ] 
     named: 'classes'; with: [
  stat dimension: [ :m | m selector] 
         named: 'methods']];
    dimension: [ :m | 
 m methodClass package mcWorkingCopy versionInfo author ] 
    named: 'authors'; with: [ 
 stat dimension: [ :m | m methodClass ] 
     named: 'pcks'; with: [
  stat dimension: [ :m | m methodClass ] 
      named: 'classes'; with: [
   stat dimension: [ :m | m selector] 
       named: 'methods']]].

  stat accumulateAll: ((RPackage allInstances 
 select: [ :each | each name beginsWith: 'Athens']) gather: #methods).
  ^stat inspect


You can load code by:
Metacello new
  baseline: 'ObjectStatistics';
  repository: 'github://dionisiydk/ObjectStatistics';
  load.
Update: project was moved to github