A simple trace utility in Pharo Smalltalk

Trace

Introdução

Várias vezes preferi usar o Inspector em vez do Transcript quando queria detalhar a evolução de algumas variáveis em sessões de debug. Uma ideia que às vezes aplicava era colocar cópias de objetos numa OrderedCollection que depois enviava para um Inspector usando a mensagem inspect. Dentro dessa idéia criei uma classe Trace que faz algo similar a isto.

Métodos para tracing

Trace methods

Os cinco métodos de classe na lista acima cobrem, ao meu ver, as necessidades básicas.

addValue:labeled

Trace addValueLabeled

O método acima é usado nos três últimos métodos da lista.

O primeiro argumento pode ser qualquer objeto que responda à mensagem #value. Na verdade todos os objetos respondem à essa mensagem diretamente ou através da hierarquia abaixo da classe Object pois herdam o método Object>>#value (Ver abaixo).

Object value

Estamos interessados em usar também BlockClosures que implementam #value diferentemente. A mensagem #value faz com que o código entre os colchetes de uma BlockClosure seja executado e retorne o resultado da última expressão. Isto permite uma flexibilidade para inspeção de resultados de trechos de código.

Cuidado! Ao fazer o tracing de uma Association deve-se colocá-la entre colchete para evitar que o seu método #value seja invocado dando resultados “inesperados”. Mas no geral a coisa funciona bem.

addValue:

Trace addValue

Este método é similar ao anterior e permite omitir o label que é gerado automaticamente.

Nota: Os dois métodos acima permitem fazer o tracing de variáveis de instância e locais. Mas o método a seguir é dedicado às variáveis locais.

addTempNamed:

Trace addTempNamed.png

Um exemplo de uso do método acima segue abaixo:

Trace addTempNamed example.png

addContext:

Trace addContext

O método acima serve para indicar o contexto onde houve sua invocação, como exemplificado abaixo:

Trace addContext example

addComment:

Trace addComment

O método acima coloca um comentário na coleção de entradas de Trace.

Um exemplo

Trace example9

The file out

Object subclass: #Trace
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Trace-Core'!
!Trace commentStamp: 'chicoary 12/25/2018 18:22' prior: 0!
Trace is a utility for get variable values in a collection for inspecting after an execution.

See Trace class examples protocol.!

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

Trace class
	instanceVariableNames: 'entries nextIntegerSuffix'!

!Trace class methodsFor: 'tracing' stamp: 'chicoary 12/29/2018 10:37'!
addComment: aString 
	^ entries add: aString! !

!Trace class methodsFor: 'tracing' stamp: 'chicoary 12/27/2018 11:51'!
addContext
	| context |
	context := thisContext.
	^ entries add: context sender! !

!Trace class methodsFor: 'tracing' stamp: 'chicoary 12/29/2018 10:48'!
addValue: aValuable labeled: aSymbolOrString
	^ entries add: aSymbolOrString asSymbol -> aValuable value veryDeepCopy! !

!Trace class methodsFor: 'tracing' stamp: 'chicoary 12/29/2018 10:47'!
addTempNamed: aSymbolOrString 
	^ self addValue: (self localValueAt: aSymbolOrString) labeled: aSymbolOrString! !

!Trace class methodsFor: 'tracing' stamp: 'chicoary 12/29/2018 10:48'!
addValue: aValuable

	^ self addValue: aValuable labeled: #noname, self nextIntegerSuffix asString! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/27/2018 08:54'!
example8
	|  x y |
	Trace reset.
	Trace entries inspect.
	x := 2.
	y := 3. 
	Trace addTempNamed: #x.
	x := x + 7.
	Trace addTempNamed: #x.
	Trace addTempNamed: #y.
	(Trace getValueAt: #x) inspect! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/27/2018 11:51'!
example
	| method |
	Trace reset.
	method := Trace addContext.
	1 to: 10 do: [ :k | 
		Trace addValue: [ 'k = {1} in method "{2}" of "{3}"' format: { k. method selector. method methodClass name }] 
	].
	Trace entries inspect.! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/28/2018 15:21'!
example4
	| assoc |
	Trace reset.
	assoc := 2 -> 3. 
	Trace addValue: [ assoc ] labeled: 'Stored a copy of 2 -> 3'.
	(assoc key: 5). 
	Trace addValue: [ assoc ] labeled: 'Stored a copy of 5 -> 3'. 
	Trace entries inspect.! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/27/2018 08:55'!
example6
	|  x y |
	Trace reset.
	x := 2.
	y := 3. 
	Trace addValue: x labeled: #first.
	Trace addValue: y labeled: #y.
	Trace entries inspect.! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/27/2018 08:54'!
example5
	|  x y |
	Trace reset.
	Trace entries inspect.
	x := 2.
	y := 3. 
	Trace addTempNamed: #x.
	Trace addTempNamed: #y.
! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/27/2018 12:05'!
example3
	| assoc |
	Trace reset.
	assoc := 2 -> 3. 
	Trace addValue: [ assoc ]. "Store a copy"
	(assoc key: 5).
	Trace addValue: [ assoc ]. "Another copy"
	Trace entries inspect.! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/27/2018 12:07'!
example7
	| x |
	Trace reset.
	x := 5.
	Trace entries inspect.
	(11 to: 100 by: 17) fold: [ :a :n | 
		Trace addTempNamed: #x. 
		Trace addTempNamed: #a. 
		Trace addTempNamed: #n.
		Trace addValue: [ a + n + x ] labeled: 'a + n + x'.
		a + n + x ] ! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/29/2018 11:01'!
example9
	|  heap removed sorted |
	sorted := OrderedCollection new.
	Trace reset.
	Trace entries inspect.
	heap := Heap new.
	Trace addComment: '======> Adding...'.
	#(9 8 7 6 5 4 3 2 1) do: [ :n | 
		heap add: n.
		Trace addTempNamed: #n.
		Trace addTempNamed: #heap.
	].
	Trace addComment: '======> Removing...'.
	[ heap isEmpty ] whileFalse: [  
		removed := heap removeFirst.
		sorted add: removed.
		Trace addValue: removed labeled: #removeFirst.
		Trace addTempNamed: #heap
	].
	Trace addTempNamed: #sorted ! !

!Trace class methodsFor: 'examples' stamp: 'chicoary 12/27/2018 11:51'!
example2
	Trace reset.
	Trace addContext.
	Trace entries inspect.! !

!Trace class methodsFor: 'accessing' stamp: 'chicoary 12/25/2018 17:33'!
nextIntegerSuffix
	^ nextIntegerSuffix := nextIntegerSuffix + 1
! !

!Trace class methodsFor: 'accessing' stamp: 'chicoary 12/25/2018 11:53'!
localValueAt: aStringOrSymbol 
	| sender |
	sender := thisContext sender sender.
	^ sender tempNamed: aStringOrSymbol asSymbol! !

!Trace class methodsFor: 'accessing' stamp: 'chicoary 12/27/2018 08:51'!
getValueAt: aSymbolOrString
	^ (entries reversed detect: [ :assoc | assoc class = Association and: [assoc key = aSymbolOrString asSymbol] ]) value! !

!Trace class methodsFor: 'accessing' stamp: 'chicoary 12/29/2018 16:02'!
entries
	^ entries! !

!Trace class methodsFor: 'initialization' stamp: 'chicoary 12/29/2018 16:02'!
reset
	nextIntegerSuffix := 0.
	entries := OrderedCollection new! !

!Trace class methodsFor: 'initialization' stamp: 'chicoary 12/25/2018 17:26'!
initialize
	self reset! !

Trace initialize!PackageManifest subclass: #ManifestTrace
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Trace'!
!ManifestTrace commentStamp: '' prior: 0!
I store metadata for this package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser!

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

ManifestTrace class
	instanceVariableNames: ''!

!ManifestTrace class methodsFor: 'code-critics' stamp: 'chicoary 12/29/2018 16:25'!
ruleRBCollectionProtocolRuleV1FalsePositive
	^ #(#(#(#RGMethodDefinition #(#'Trace class' #example9 #true)) #'2018-12-29T10:55:58.930128-03:00') #(#(#RGMethodDefinition #(#'Trace class' #example10 #true)) #'2018-12-29T16:25:18.958172-03:00') )! !

!ManifestTrace class methodsFor: 'code-critics' stamp: 'chicoary 12/29/2018 10:56'!
ruleRBRefersToClassRuleV1FalsePositive
	^ #(#(#(#RGMethodDefinition #(#'Trace class' #example9 #true)) #'2018-12-29T10:56:08.460228-03:00') )! !

!ManifestTrace class methodsFor: 'code-critics' stamp: 'chicoary 12/20/2018 19:27'!
ruleRBExtraBlockRuleV1FalsePositive
	^ #(#(#(#RGMethodDefinition #(#'Trace class' #at: #true)) #'2018-12-20T19:27:31.317883-03:00') )! !
TestCase subclass: #TraceTests
	instanceVariableNames: 'y'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Trace-Core-Tests'!
!TraceTests commentStamp: 'chicoary 12/25/2018 13:41' prior: 0!
TraceTests!

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/28/2018 15:24'!
testAddValueLabeled
	| x |
	Trace reset.
	x := 999.
	Trace addValue: [ x + 1 ] labeled: 'x + 1'.
	self assert: (Trace entries anyOne) equals: #'x + 1' -> (x + 1).! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/27/2018 09:01'!
testAddValue
	| x |
	Trace reset.
	x := 999.
	Trace addValue: [ x ].
	Trace addValue: [ x + 1 ].
	self assert: Trace entries asArray equals: { #noname1 -> x. #noname2 -> (x + 1) }.
	! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/27/2018 08:57'!
testIndependentCopies
	| x |
	Trace reset.
	x := #(1 2 3).
	Trace addValue: [ x ].
   "some code"
	Trace addValue: [ x ].
	self assert: (Trace entries at: 1) value equals: (Trace entries at: 2) value.
	self deny: (Trace entries at: 1) value == (Trace entries at: 2) value.! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/28/2018 13:00'!
testAddValueWithArray

	| a |
	Trace reset.
	a := { 1->'one'. 2->'two'. 3->'three'. 4->'four' }.
	Trace addValue: a.
	(a at: 2) key: 5; value: 'five'.
	Trace addValue: a.

	self assert: Trace entries asArray 
	equals: { #noname1 ->  { 1->'one'. 2->'two'. 3->'three'. 4->'four' }. #noname2 ->  { 1->'one'. 5->'five'. 3->'three'. 4->'four' } }.

	! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/27/2018 09:08'!
testInstanceVariable
	y := 999.
	Trace reset.
	Trace addValue: [ y ] labeled: #y.
	self assert: (Trace entries anyOne) equals: #y -> y.! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/27/2018 09:12'!
testAddTempNamed
	| x |
	x := 999.
	Trace reset.
	Trace addTempNamed: #x.
	self assert: (Trace entries anyOne) equals: #x -> x.! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/29/2018 10:51'!
testAddComment
	Trace reset.
	Trace addComment: 'A comment'.
	self assert: (Trace entries anyOne asString) equals: 'A comment'.! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/27/2018 11:51'!
testGetValueAt
	| x |
	Trace reset.
	x := 3.
	Trace addTempNamed: #x.
	Trace addContext.
	x := 5.
	Trace addTempNamed: #x.
	self assert: (Trace getValueAt: #x) equals: 5.! !

!TraceTests methodsFor: 'tests' stamp: 'chicoary 12/27/2018 11:52'!
testAddContext
	Trace reset.
	Trace addContext.
	self assert: (Trace entries anyOne asString) equals: 'TraceTests>>testAddContext'.! !

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s