The Inbox: Morphic-ct.1540.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: Morphic-ct.1540.mcz

commits-2
A new version of Morphic was added to project The Inbox:
http://source.squeak.org/inbox/Morphic-ct.1540.mcz

==================== Summary ====================

Name: Morphic-ct.1540
Author: ct
Time: 23 September 2019, 4:44:32.818523 pm
UUID: 064f7940-90ea-1646-a6f5-6c0a82c06e91
Ancestors: Morphic-mt.1539

Proposal: Remember & explore provenance of any morph.

Sure, this is a performance regression (that's why it's turned off by default), but when you are debugging, I could tell you of so many cases in which this can be a useful utility. Looking at #browseCreatorMethod, we can also use this to allow beginners for quickly browsing the <example> method that created a morph.

=============== Diff against Morphic-mt.1539 ===============

Item was changed:
  Object subclass: #Morph
  instanceVariableNames: 'bounds owner submorphs fullBounds color extension'
+ classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius RememberProvenance UseSoftDropShadow'
- classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius UseSoftDropShadow'
  poolDictionaries: ''
  category: 'Morphic-Kernel'!
 
  !Morph commentStamp: 'klc 3/14/2017 11:30' prior: 0!
  A Morph (from the Greek "shape" or "form") is an interactive graphical object. General information on the Morphic system can be found at http://wiki.squeak.org/squeak/30.
 
  Morphs exist in a tree, rooted at a World (generally a PasteUpMorph). The morphs owned by a morph are its submorphs. Morphs are drawn recursively; if a Morph has no owner it never gets drawn. To hide a Morph and its submorphs, set its #visible property to false using the #visible: method.
 
  The World (screen) coordinate system is used for most coordinates, but can be changed if there is a TransformMorph somewhere in the owner chain.
 
  My instance variables have accessor methods (e.g., #bounds, #bounds:). Most users should use the accessor methods instead of using the instance variables directly.
 
  Structure:
  instance var Type Description
  bounds Rectangle A Rectangle indicating my position and a size that will enclose me.
  owner Morph My parent Morph, or nil for the top-level Morph, which is a
    or nil world, typically a PasteUpMorph.
  submorphs Array My child Morphs.
  fullBounds Rectangle A Rectangle minimally enclosing me and my submorphs.
  color Color My primary color. Subclasses can use this in different ways.
  extension MorphExtension Allows extra properties to be stored without adding a
  or nil   storage burden to all morphs.
 
  By default, Morphs do not position their submorphs. Morphs may position their submorphs directly or use a LayoutPolicy to automatically control their submorph positioning.
 
  Although Morph has some support for BorderStyle, most users should use BorderedMorph if they want borders.!

Item was added:
+ ----- Method: Morph class>>rememberProvenance (in category 'preferences') -----
+ rememberProvenance
+
+ <preference: 'Remember provenance of each morph'
+ categoryList: #(Morphic)
+ description: 'If enabled, each morph will contain a debug item to view its creator stack. This allows you to explore its provenance later. May affect performance.'
+ type: #Boolean>
+ ^ RememberProvenance ifNil: [false]!

Item was added:
+ ----- Method: Morph class>>rememberProvenance: (in category 'preferences') -----
+ rememberProvenance: aBoolean
+
+ RememberProvenance := aBoolean.!

Item was added:
+ ----- Method: Morph>>browseCreatorMethod (in category 'debug and other') -----
+ browseCreatorMethod
+
+ ^ (self valueOfProperty: #officialCreator) browse!

Item was changed:
  ----- Method: Morph>>buildDebugMenu: (in category 'debug and other') -----
  buildDebugMenu: aHand
  "Answer a debugging menu for the receiver.  The hand argument is seemingly historical and plays no role presently"
 
  | aMenu aPlayer |
  aMenu := MenuMorph new defaultTarget: self.
  aMenu addStayUpItem.
  (self hasProperty: #errorOnDraw) ifTrue:
  [aMenu add: 'start drawing again' translated action: #resumeAfterDrawError.
  aMenu addLine].
  (self hasProperty: #errorOnStep) ifTrue:
  [aMenu add: 'start stepping again' translated action: #resumeAfterStepError.
  aMenu addLine].
 
  aMenu add: 'inspect morph' translated action: #inspectInMorphic:.
  aMenu add: 'inspect owner chain' translated action: #inspectOwnerChain.
  Smalltalk isMorphic ifFalse:
  [aMenu add: 'inspect morph (in MVC)' translated action: #inspect].
 
  self isMorphicModel ifTrue:
  [aMenu add: 'inspect model' translated target: self model action: #inspect;
  add: 'explore model' translated target: self model action: #explore].
  (aPlayer := self player) ifNotNil:
  [aMenu add: 'inspect player' translated target: aPlayer action: #inspect].
 
       aMenu add: 'explore morph' translated target: self selector: #exploreInMorphic:.
 
  aMenu addLine.
  aPlayer ifNotNil:
  [ aMenu add: 'viewer for Player' translated target: self player action: #beViewed.
  aMenu balloonTextForLastItem: 'Opens a viewer on my Player -- this is the same thing you get if you click on the cyan "View" halo handle' translated ].
 
  aMenu add: 'viewer for Morph' translated target: self action: #viewMorphDirectly.
  aMenu balloonTextForLastItem: 'Opens a Viewer on this Morph, rather than on its Player' translated.
  aMenu addLine.
 
  aPlayer ifNotNil:
  [aPlayer class isUniClass ifTrue: [
  aMenu add: 'browse player class' translated target: aPlayer selector: #haveFullProtocolBrowsedShowingSelector: argumentList: #(nil)]].
  aMenu add: 'browse morph class' translated target: self selector: #browseHierarchy.
+ self isMorphicModel
- (self isMorphicModel)
  ifTrue: [aMenu
  add: 'browse model class'
  target: self model
  selector: #browseHierarchy].
+ (self valueOfProperty: #officialCreator ifAbsent: [nil]) ifNotNil: [
+ aMenu add: 'browse creator method' action: #browseCreatorMethod].
+ (self hasProperty: #creatorStack) ifTrue: [
+ aMenu add: 'explore creator stack' action: #exploreCreatorStack].
  aMenu addLine.
 
  self addViewingItemsTo: aMenu.
  aMenu
  add: 'make own subclass' translated action: #subclassMorph;
  add: 'save morph in file' translated  action: #saveOnFile;
  addLine;
  add: 'call #tempCommand' translated action: #tempCommand;
  add: 'define #tempCommand' translated action: #defineTempCommand;
  addLine;
 
  add: 'control-menu...' translated target: self selector: #invokeMetaMenu:;
  add: 'edit balloon help' translated action: #editBalloonHelpText.
 
  ^ aMenu!

Item was added:
+ ----- Method: Morph>>exploreCreatorStack (in category 'debug and other') -----
+ exploreCreatorStack
+
+ ^ (self valueOfProperty: #creatorStack) explore!

Item was changed:
  ----- Method: Morph>>initialize (in category 'initialization') -----
  initialize
  "initialize the state of the receiver"
  owner := nil.
  submorphs := Array empty.
  bounds := self defaultBounds.
+ color := self defaultColor.
+ self class rememberProvenance ifTrue: [
+ | creatorStack |
+ creatorStack := thisContext home stack collect: #method.
+ self setProperty: #creatorStack toValue: creatorStack.
+ creatorStack
+ detect: [:method | method hasPragma: #example]
+ ifFound: [:method | self setProperty: #officialCreator toValue: method]].!
- color := self defaultColor!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Christoph Thiede

How do you like that?


Can you notice a real performance impact when activating the preference? What is on Raspi?


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Montag, 23. September 2019 16:44:39
An: [hidden email]
Betreff: [squeak-dev] The Inbox: Morphic-ct.1540.mcz
 
A new version of Morphic was added to project The Inbox:
http://source.squeak.org/inbox/Morphic-ct.1540.mcz

==================== Summary ====================

Name: Morphic-ct.1540
Author: ct
Time: 23 September 2019, 4:44:32.818523 pm
UUID: 064f7940-90ea-1646-a6f5-6c0a82c06e91
Ancestors: Morphic-mt.1539

Proposal: Remember & explore provenance of any morph.

Sure, this is a performance regression (that's why it's turned off by default), but when you are debugging, I could tell you of so many cases in which this can be a useful utility. Looking at #browseCreatorMethod, we can also use this to allow beginners for quickly browsing the <example> method that created a morph.

=============== Diff against Morphic-mt.1539 ===============

Item was changed:
  Object subclass: #Morph
         instanceVariableNames: 'bounds owner submorphs fullBounds color extension'
+        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius RememberProvenance UseSoftDropShadow'
-        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius UseSoftDropShadow'
         poolDictionaries: ''
         category: 'Morphic-Kernel'!
 
  !Morph commentStamp: 'klc 3/14/2017 11:30' prior: 0!
  A Morph (from the Greek "shape" or "form") is an interactive graphical object. General information on the Morphic system can be found at http://wiki.squeak.org/squeak/30.
 
  Morphs exist in a tree, rooted at a World (generally a PasteUpMorph). The morphs owned by a morph are its submorphs. Morphs are drawn recursively; if a Morph has no owner it never gets drawn. To hide a Morph and its submorphs, set its #visible property to false using the #visible: method.
 
  The World (screen) coordinate system is used for most coordinates, but can be changed if there is a TransformMorph somewhere in the owner chain.
 
  My instance variables have accessor methods (e.g., #bounds, #bounds:). Most users should use the accessor methods instead of using the instance variables directly.
 
  Structure:
  instance var   Type                     Description
  bounds                         Rectangle                A Rectangle indicating my position and a size that will enclose                                                                  me.
  owner                  Morph             My parent Morph, or nil for the top-level Morph, which is a
                                 or nil                  world, typically a PasteUpMorph.
  submorphs              Array                    My child Morphs.
  fullBounds             Rectangle                A Rectangle minimally enclosing me and my submorphs.
  color                  Color                    My primary color. Subclasses can use this in different ways.
  extension              MorphExtension Allows extra properties to be stored without adding a
                                 or nil                           storage burden to all morphs.
 
  By default, Morphs do not position their submorphs. Morphs may position their submorphs directly or use a LayoutPolicy to automatically control their submorph positioning.
 
  Although Morph has some support for BorderStyle, most users should use BorderedMorph if they want borders.!

Item was added:
+ ----- Method: Morph class>>rememberProvenance (in category 'preferences') -----
+ rememberProvenance
+
+        <preference: 'Remember provenance of each morph'
+                categoryList: #(Morphic)
+                description: 'If enabled, each morph will contain a debug item to view its creator stack. This allows you to explore its provenance later. May affect performance.'
+                type: #Boolean>
+        ^ RememberProvenance ifNil: [false]!

Item was added:
+ ----- Method: Morph class>>rememberProvenance: (in category 'preferences') -----
+ rememberProvenance: aBoolean
+
+        RememberProvenance := aBoolean.!

Item was added:
+ ----- Method: Morph>>browseCreatorMethod (in category 'debug and other') -----
+ browseCreatorMethod
+
+        ^ (self valueOfProperty: #officialCreator) browse!

Item was changed:
  ----- Method: Morph>>buildDebugMenu: (in category 'debug and other') -----
  buildDebugMenu: aHand
         "Answer a debugging menu for the receiver.  The hand argument is seemingly historical and plays no role presently"
 
         | aMenu aPlayer |
         aMenu := MenuMorph new defaultTarget: self.
         aMenu addStayUpItem.
         (self hasProperty: #errorOnDraw) ifTrue:
                 [aMenu add: 'start drawing again' translated action: #resumeAfterDrawError.
                 aMenu addLine].
         (self hasProperty: #errorOnStep) ifTrue:
                 [aMenu add: 'start stepping again' translated action: #resumeAfterStepError.
                 aMenu addLine].
 
         aMenu add: 'inspect morph' translated action: #inspectInMorphic:.
         aMenu add: 'inspect owner chain' translated action: #inspectOwnerChain.
         Smalltalk isMorphic ifFalse:
                 [aMenu add: 'inspect morph (in MVC)' translated action: #inspect].
 
         self isMorphicModel ifTrue:
                 [aMenu add: 'inspect model' translated target: self model action: #inspect;
                         add: 'explore model' translated target: self model action: #explore].
         (aPlayer := self player) ifNotNil:
                 [aMenu add: 'inspect player' translated target: aPlayer action: #inspect].
 
       aMenu add: 'explore morph' translated target: self selector: #exploreInMorphic:.
 
         aMenu addLine.
         aPlayer ifNotNil:
                 [ aMenu add: 'viewer for Player' translated target: self player action: #beViewed.
         aMenu balloonTextForLastItem: 'Opens a viewer on my Player -- this is the same thing you get if you click on the cyan "View" halo handle' translated ].
 
         aMenu add: 'viewer for Morph' translated target: self action: #viewMorphDirectly.
         aMenu balloonTextForLastItem: 'Opens a Viewer on this Morph, rather than on its Player' translated.
         aMenu addLine.
 
         aPlayer ifNotNil:
                 [aPlayer class isUniClass ifTrue: [
                         aMenu add: 'browse player class' translated target: aPlayer selector: #haveFullProtocolBrowsedShowingSelector: argumentList: #(nil)]].
         aMenu add: 'browse morph class' translated target: self selector: #browseHierarchy.
+        self isMorphicModel
-        (self isMorphicModel)
                 ifTrue: [aMenu
                                 add: 'browse model class'
                                 target: self model
                                 selector: #browseHierarchy].
+        (self valueOfProperty: #officialCreator ifAbsent: [nil]) ifNotNil: [
+                aMenu add: 'browse creator method' action: #browseCreatorMethod].
+        (self hasProperty: #creatorStack) ifTrue: [
+                aMenu add: 'explore creator stack' action: #exploreCreatorStack].
         aMenu addLine.
 
         self addViewingItemsTo: aMenu.
         aMenu
                 add: 'make own subclass' translated action: #subclassMorph;
                 add: 'save morph in file' translated  action: #saveOnFile;
                 addLine;
                 add: 'call #tempCommand' translated action: #tempCommand;
                 add: 'define #tempCommand' translated action: #defineTempCommand;
                 addLine;
 
                 add: 'control-menu...' translated target: self selector: #invokeMetaMenu:;
                 add: 'edit balloon help' translated action: #editBalloonHelpText.
 
         ^ aMenu!

Item was added:
+ ----- Method: Morph>>exploreCreatorStack (in category 'debug and other') -----
+ exploreCreatorStack
+
+        ^ (self valueOfProperty: #creatorStack) explore!

Item was changed:
  ----- Method: Morph>>initialize (in category 'initialization') -----
  initialize
         "initialize the state of the receiver"
         owner := nil.
         submorphs := Array empty.
         bounds := self defaultBounds.
+        color := self defaultColor.
+        self class rememberProvenance ifTrue: [
+                | creatorStack |
+                creatorStack := thisContext home stack collect: #method.
+                self setProperty: #creatorStack toValue: creatorStack.
+                creatorStack
+                        detect: [:method | method hasPragma: #example]
+                        ifFound: [:method | self setProperty: #officialCreator toValue: method]].!
-        color := self defaultColor!




Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

marcel.taeumel
Hmm... this feature has quite an impact on Morph creation time and the overall memory footprint, right? Did you benchmark that somehow?

From a first impression, I would say:

-1 to keeping track of the #creatorStack because compiled methods can outdate in a long-running image
+1 to keeping track of the #officialCreator because that could help setting a breakpoint at the right place

Still ... what if we would use PasteUpMorph >> addedMorph:? How would those stacks look like? What's the user requirements here? Something in the world or more a debugging tool at the level of halos?

Maybe there could be a way to keep track of morph creation explicitely?

Morph keepTrackOfCreationDuring: someBlock.

Even with a preference, adding such a thing to Morph >> #initialize requires actual benchmarks to asses the performance and memory overhead.

Just my two cents. :-)

Best,
Marcel

Am 23.09.2019 16:47:29 schrieb Thiede, Christoph <[hidden email]>:

How do you like that?


Can you notice a real performance impact when activating the preference? What is on Raspi?


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Montag, 23. September 2019 16:44:39
An: [hidden email]
Betreff: [squeak-dev] The Inbox: Morphic-ct.1540.mcz
 
A new version of Morphic was added to project The Inbox:
http://source.squeak.org/inbox/Morphic-ct.1540.mcz

==================== Summary ====================

Name: Morphic-ct.1540
Author: ct
Time: 23 September 2019, 4:44:32.818523 pm
UUID: 064f7940-90ea-1646-a6f5-6c0a82c06e91
Ancestors: Morphic-mt.1539

Proposal: Remember & explore provenance of any morph.

Sure, this is a performance regression (that's why it's turned off by default), but when you are debugging, I could tell you of so many cases in which this can be a useful utility. Looking at #browseCreatorMethod, we can also use this to allow beginners for quickly browsing the <example> method that created a morph.

=============== Diff against Morphic-mt.1539 ===============

Item was changed:
  Object subclass: #Morph
         instanceVariableNames: 'bounds owner submorphs fullBounds color extension'
+        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius RememberProvenance UseSoftDropShadow'
-        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius UseSoftDropShadow'
         poolDictionaries: ''
         category: 'Morphic-Kernel'!
 
  !Morph commentStamp: 'klc 3/14/2017 11:30' prior: 0!
  A Morph (from the Greek "shape" or "form") is an interactive graphical object. General information on the Morphic system can be found at http://wiki.squeak.org/squeak/30.
 
  Morphs exist in a tree, rooted at a World (generally a PasteUpMorph). The morphs owned by a morph are its submorphs. Morphs are drawn recursively; if a Morph has no owner it never gets drawn. To hide a Morph and its submorphs, set its #visible property to false using the #visible: method.
 
  The World (screen) coordinate system is used for most coordinates, but can be changed if there is a TransformMorph somewhere in the owner chain.
 
  My instance variables have accessor methods (e.g., #bounds, #bounds:). Most users should use the accessor methods instead of using the instance variables directly.
 
  Structure:
  instance var   Type                     Description
  bounds                         Rectangle                A Rectangle indicating my position and a size that will enclose                                                                  me.
  owner                  Morph             My parent Morph, or nil for the top-level Morph, which is a
                                 or nil                  world, typically a PasteUpMorph.
  submorphs              Array                    My child Morphs.
  fullBounds             Rectangle                A Rectangle minimally enclosing me and my submorphs.
  color                  Color                    My primary color. Subclasses can use this in different ways.
  extension              MorphExtension Allows extra properties to be stored without adding a
                                 or nil                           storage burden to all morphs.
 
  By default, Morphs do not position their submorphs. Morphs may position their submorphs directly or use a LayoutPolicy to automatically control their submorph positioning.
 
  Although Morph has some support for BorderStyle, most users should use BorderedMorph if they want borders.!

Item was added:
+ ----- Method: Morph class>>rememberProvenance (in category 'preferences') -----
+ rememberProvenance
+
+        <preference: 'Remember provenance of each morph'
+                categoryList: #(Morphic)
+                description: 'If enabled, each morph will contain a debug item to view its creator stack. This allows you to explore its provenance later. May affect performance.'
+                type: #Boolean>
+        ^ RememberProvenance ifNil: [false]!

Item was added:
+ ----- Method: Morph class>>rememberProvenance: (in category 'preferences') -----
+ rememberProvenance: aBoolean
+
+        RememberProvenance := aBoolean.!

Item was added:
+ ----- Method: Morph>>browseCreatorMethod (in category 'debug and other') -----
+ browseCreatorMethod
+
+        ^ (self valueOfProperty: #officialCreator) browse!

Item was changed:
  ----- Method: Morph>>buildDebugMenu: (in category 'debug and other') -----
  buildDebugMenu: aHand
         "Answer a debugging menu for the receiver.  The hand argument is seemingly historical and plays no role presently"
 
         | aMenu aPlayer |
         aMenu := MenuMorph new defaultTarget: self.
         aMenu addStayUpItem.
         (self hasProperty: #errorOnDraw) ifTrue:
                 [aMenu add: 'start drawing again' translated action: #resumeAfterDrawError.
                 aMenu addLine].
         (self hasProperty: #errorOnStep) ifTrue:
                 [aMenu add: 'start stepping again' translated action: #resumeAfterStepError.
                 aMenu addLine].
 
         aMenu add: 'inspect morph' translated action: #inspectInMorphic:.
         aMenu add: 'inspect owner chain' translated action: #inspectOwnerChain.
         Smalltalk isMorphic ifFalse:
                 [aMenu add: 'inspect morph (in MVC)' translated action: #inspect].
 
         self isMorphicModel ifTrue:
                 [aMenu add: 'inspect model' translated target: self model action: #inspect;
                         add: 'explore model' translated target: self model action: #explore].
         (aPlayer := self player) ifNotNil:
                 [aMenu add: 'inspect player' translated target: aPlayer action: #inspect].
 
       aMenu add: 'explore morph' translated target: self selector: #exploreInMorphic:.
 
         aMenu addLine.
         aPlayer ifNotNil:
                 [ aMenu add: 'viewer for Player' translated target: self player action: #beViewed.
         aMenu balloonTextForLastItem: 'Opens a viewer on my Player -- this is the same thing you get if you click on the cyan "View" halo handle' translated ].
 
         aMenu add: 'viewer for Morph' translated target: self action: #viewMorphDirectly.
         aMenu balloonTextForLastItem: 'Opens a Viewer on this Morph, rather than on its Player' translated.
         aMenu addLine.
 
         aPlayer ifNotNil:
                 [aPlayer class isUniClass ifTrue: [
                         aMenu add: 'browse player class' translated target: aPlayer selector: #haveFullProtocolBrowsedShowingSelector: argumentList: #(nil)]].
         aMenu add: 'browse morph class' translated target: self selector: #browseHierarchy.
+        self isMorphicModel
-        (self isMorphicModel)
                 ifTrue: [aMenu
                                 add: 'browse model class'
                                 target: self model
                                 selector: #browseHierarchy].
+        (self valueOfProperty: #officialCreator ifAbsent: [nil]) ifNotNil: [
+                aMenu add: 'browse creator method' action: #browseCreatorMethod].
+        (self hasProperty: #creatorStack) ifTrue: [
+                aMenu add: 'explore creator stack' action: #exploreCreatorStack].
         aMenu addLine.
 
         self addViewingItemsTo: aMenu.
         aMenu
                 add: 'make own subclass' translated action: #subclassMorph;
                 add: 'save morph in file' translated  action: #saveOnFile;
                 addLine;
                 add: 'call #tempCommand' translated action: #tempCommand;
                 add: 'define #tempCommand' translated action: #defineTempCommand;
                 addLine;
 
                 add: 'control-menu...' translated target: self selector: #invokeMetaMenu:;
                 add: 'edit balloon help' translated action: #editBalloonHelpText.
 
         ^ aMenu!

Item was added:
+ ----- Method: Morph>>exploreCreatorStack (in category 'debug and other') -----
+ exploreCreatorStack
+
+        ^ (self valueOfProperty: #creatorStack) explore!

Item was changed:
  ----- Method: Morph>>initialize (in category 'initialization') -----
  initialize
         "initialize the state of the receiver"
         owner := nil.
         submorphs := Array empty.
         bounds := self defaultBounds.
+        color := self defaultColor.
+        self class rememberProvenance ifTrue: [
+                | creatorStack |
+                creatorStack := thisContext home stack collect: #method.
+                self setProperty: #creatorStack toValue: creatorStack.
+                creatorStack
+                        detect: [:method | method hasPragma: #example]
+                        ifFound: [:method | self setProperty: #officialCreator toValue: method]].!
-        color := self defaultColor!




Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Christoph Thiede

Thanks for your thoughts!


Well, I already did some benchmarks, but I find it hard to achieve objective results:


cleanUp := [
    TransformationMorph allSubInstancesDo: [:m | m submorphs ifEmpty: [m delete]].
    5 timesRepeat: [Smalltalk garbageCollect]].


Morph rememberProvenance: false.
cleanUp value.
[TransformationMorph new] bench." '526,000 per second. 1.9 microseconds per run.'"

"Test 2"
Morph rememberProvenance: true.
cleanUp value.
[TransformationMorph new] bench." '118,000 per second. 8.44 microseconds per run.'"


Morph rememberProvenance: false.
cleanUp value.
[TransformationMorph new openInWorld] bench." '3,790 per second. 264 microseconds per run.'"

Morph rememberProvenance: true.
cleanUp value.
[TransformationMorph new openInWorld] bench." '3,690 per second. 271 microseconds per run.'"


Could we say: the overhead nearly disappears when compared to the costs of actually opening a morph?

How could I measure the memory load appropriately?


> -1 to keeping track of the #creatorStack because compiled methods can outdate in a long-running image

See Tools-ct.894, you actually could browse the outdated methods, but I agree this is of very rare benefit. I changed the code to store MethodReferences instead, but if my benchmark code is not erroneous, this nearly doubled the time for test 2 (15.9 ms/run).

Still ... what if we would use PasteUpMorph >> addedMorph:? How would those stacks look like? What's the user requirements here? Something in the world or more a debugging tool at the level of halos?


My main intention was to allow users for finding out where a morph was created. Where is the code that is responsible for showing this Debugger window? What methods create the docking bar morph? How is the window close box created? If we only record where it was added to world, we are missing any information about how exactly it was created, not to mention information for any submorphs.


Morph keepTrackOfCreationDuring: someBlock.

+1 :-)

So you mean all I need to update is the class-side of Morph?


Best,

Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Montag, 23. September 2019 17:12 Uhr
An: Alan Grimes via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1540.mcz
 
Hmm... this feature has quite an impact on Morph creation time and the overall memory footprint, right? Did you benchmark that somehow?

From a first impression, I would say:

-1 to keeping track of the #creatorStack because compiled methods can outdate in a long-running image
+1 to keeping track of the #officialCreator because that could help setting a breakpoint at the right place

Still ... what if we would use PasteUpMorph >> addedMorph:? How would those stacks look like? What's the user requirements here? Something in the world or more a debugging tool at the level of halos?

Maybe there could be a way to keep track of morph creation explicitely?

Morph keepTrackOfCreationDuring: someBlock.

Even with a preference, adding such a thing to Morph >> #initialize requires actual benchmarks to asses the performance and memory overhead.

Just my two cents. :-)

Best,
Marcel

Am 23.09.2019 16:47:29 schrieb Thiede, Christoph <[hidden email]>:

How do you like that?


Can you notice a real performance impact when activating the preference? What is on Raspi?


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Montag, 23. September 2019 16:44:39
An: [hidden email]
Betreff: [squeak-dev] The Inbox: Morphic-ct.1540.mcz
 
A new version of Morphic was added to project The Inbox:
http://source.squeak.org/inbox/Morphic-ct.1540.mcz

==================== Summary ====================

Name: Morphic-ct.1540
Author: ct
Time: 23 September 2019, 4:44:32.818523 pm
UUID: 064f7940-90ea-1646-a6f5-6c0a82c06e91
Ancestors: Morphic-mt.1539

Proposal: Remember & explore provenance of any morph.

Sure, this is a performance regression (that's why it's turned off by default), but when you are debugging, I could tell you of so many cases in which this can be a useful utility. Looking at #browseCreatorMethod, we can also use this to allow beginners for quickly browsing the <example> method that created a morph.

=============== Diff against Morphic-mt.1539 ===============

Item was changed:
  Object subclass: #Morph
         instanceVariableNames: 'bounds owner submorphs fullBounds color extension'
+        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius RememberProvenance UseSoftDropShadow'
-        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius UseSoftDropShadow'
         poolDictionaries: ''
         category: 'Morphic-Kernel'!
 
  !Morph commentStamp: 'klc 3/14/2017 11:30' prior: 0!
  A Morph (from the Greek "shape" or "form") is an interactive graphical object. General information on the Morphic system can be found at http://wiki.squeak.org/squeak/30.
 
  Morphs exist in a tree, rooted at a World (generally a PasteUpMorph). The morphs owned by a morph are its submorphs. Morphs are drawn recursively; if a Morph has no owner it never gets drawn. To hide a Morph and its submorphs, set its #visible property to false using the #visible: method.
 
  The World (screen) coordinate system is used for most coordinates, but can be changed if there is a TransformMorph somewhere in the owner chain.
 
  My instance variables have accessor methods (e.g., #bounds, #bounds:). Most users should use the accessor methods instead of using the instance variables directly.
 
  Structure:
  instance var   Type                     Description
  bounds                         Rectangle                A Rectangle indicating my position and a size that will enclose                                                                  me.
  owner                  Morph             My parent Morph, or nil for the top-level Morph, which is a
                                 or nil                  world, typically a PasteUpMorph.
  submorphs              Array                    My child Morphs.
  fullBounds             Rectangle                A Rectangle minimally enclosing me and my submorphs.
  color                  Color                    My primary color. Subclasses can use this in different ways.
  extension              MorphExtension Allows extra properties to be stored without adding a
                                 or nil                           storage burden to all morphs.
 
  By default, Morphs do not position their submorphs. Morphs may position their submorphs directly or use a LayoutPolicy to automatically control their submorph positioning.
 
  Although Morph has some support for BorderStyle, most users should use BorderedMorph if they want borders.!

Item was added:
+ ----- Method: Morph class>>rememberProvenance (in category 'preferences') -----
+ rememberProvenance
+
+        <preference: 'Remember provenance of each morph'
+                categoryList: #(Morphic)
+                description: 'If enabled, each morph will contain a debug item to view its creator stack. This allows you to explore its provenance later. May affect performance.'
+                type: #Boolean>
+        ^ RememberProvenance ifNil: [false]!

Item was added:
+ ----- Method: Morph class>>rememberProvenance: (in category 'preferences') -----
+ rememberProvenance: aBoolean
+
+        RememberProvenance := aBoolean.!

Item was added:
+ ----- Method: Morph>>browseCreatorMethod (in category 'debug and other') -----
+ browseCreatorMethod
+
+        ^ (self valueOfProperty: #officialCreator) browse!

Item was changed:
  ----- Method: Morph>>buildDebugMenu: (in category 'debug and other') -----
  buildDebugMenu: aHand
         "Answer a debugging menu for the receiver.  The hand argument is seemingly historical and plays no role presently"
 
         | aMenu aPlayer |
         aMenu := MenuMorph new defaultTarget: self.
         aMenu addStayUpItem.
         (self hasProperty: #errorOnDraw) ifTrue:
                 [aMenu add: 'start drawing again' translated action: #resumeAfterDrawError.
                 aMenu addLine].
         (self hasProperty: #errorOnStep) ifTrue:
                 [aMenu add: 'start stepping again' translated action: #resumeAfterStepError.
                 aMenu addLine].
 
         aMenu add: 'inspect morph' translated action: #inspectInMorphic:.
         aMenu add: 'inspect owner chain' translated action: #inspectOwnerChain.
         Smalltalk isMorphic ifFalse:
                 [aMenu add: 'inspect morph (in MVC)' translated action: #inspect].
 
         self isMorphicModel ifTrue:
                 [aMenu add: 'inspect model' translated target: self model action: #inspect;
                         add: 'explore model' translated target: self model action: #explore].
         (aPlayer := self player) ifNotNil:
                 [aMenu add: 'inspect player' translated target: aPlayer action: #inspect].
 
       aMenu add: 'explore morph' translated target: self selector: #exploreInMorphic:.
 
         aMenu addLine.
         aPlayer ifNotNil:
                 [ aMenu add: 'viewer for Player' translated target: self player action: #beViewed.
         aMenu balloonTextForLastItem: 'Opens a viewer on my Player -- this is the same thing you get if you click on the cyan "View" halo handle' translated ].
 
         aMenu add: 'viewer for Morph' translated target: self action: #viewMorphDirectly.
         aMenu balloonTextForLastItem: 'Opens a Viewer on this Morph, rather than on its Player' translated.
         aMenu addLine.
 
         aPlayer ifNotNil:
                 [aPlayer class isUniClass ifTrue: [
                         aMenu add: 'browse player class' translated target: aPlayer selector: #haveFullProtocolBrowsedShowingSelector: argumentList: #(nil)]].
         aMenu add: 'browse morph class' translated target: self selector: #browseHierarchy.
+        self isMorphicModel
-        (self isMorphicModel)
                 ifTrue: [aMenu
                                 add: 'browse model class'
                                 target: self model
                                 selector: #browseHierarchy].
+        (self valueOfProperty: #officialCreator ifAbsent: [nil]) ifNotNil: [
+                aMenu add: 'browse creator method' action: #browseCreatorMethod].
+        (self hasProperty: #creatorStack) ifTrue: [
+                aMenu add: 'explore creator stack' action: #exploreCreatorStack].
         aMenu addLine.
 
         self addViewingItemsTo: aMenu.
         aMenu
                 add: 'make own subclass' translated action: #subclassMorph;
                 add: 'save morph in file' translated  action: #saveOnFile;
                 addLine;
                 add: 'call #tempCommand' translated action: #tempCommand;
                 add: 'define #tempCommand' translated action: #defineTempCommand;
                 addLine;
 
                 add: 'control-menu...' translated target: self selector: #invokeMetaMenu:;
                 add: 'edit balloon help' translated action: #editBalloonHelpText.
 
         ^ aMenu!

Item was added:
+ ----- Method: Morph>>exploreCreatorStack (in category 'debug and other') -----
+ exploreCreatorStack
+
+        ^ (self valueOfProperty: #creatorStack) explore!

Item was changed:
  ----- Method: Morph>>initialize (in category 'initialization') -----
  initialize
         "initialize the state of the receiver"
         owner := nil.
         submorphs := Array empty.
         bounds := self defaultBounds.
+        color := self defaultColor.
+        self class rememberProvenance ifTrue: [
+                | creatorStack |
+                creatorStack := thisContext home stack collect: #method.
+                self setProperty: #creatorStack toValue: creatorStack.
+                creatorStack
+                        detect: [:method | method hasPragma: #example]
+                        ifFound: [:method | self setProperty: #officialCreator toValue: method]].!
-        color := self defaultColor!




Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

timrowledge
Remember that gazillions of Morphs get created (take a look at the building of a menu; scary). 'not a lot' times 'gazillions' can easily add up to 'I hate this treacle speed UI'.


tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Maybe Computer Science should be in the College of Theology



Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Stéphane Rollandin
In reply to this post by commits-2
Le 23/09/2019 à 14:44, [hidden email] a écrit :

> A new version of Morphic was added to project The Inbox:
> http://source.squeak.org/inbox/Morphic-ct.1540.mcz
>
> ==================== Summary ====================
>
> Name: Morphic-ct.1540
> Author: ct
> Time: 23 September 2019, 4:44:32.818523 pm
> UUID: 064f7940-90ea-1646-a6f5-6c0a82c06e91
> Ancestors: Morphic-mt.1539
>
> Proposal: Remember & explore provenance of any morph.

-1

Please do not add any kludge to Morphs - they are already slow and fat.
Morphs are the basic components of any morphic thing, we should be able
to handle *a lot* of them with the less possible overhead.

Stef


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Christoph Thiede

Do you also refer to Marcel's proposal, which I implemented in Morphic-ct.1541? This does not affect normal Morphic use at all, unless you use explicitly call #rememberProvenanceDuring:.



Von: Squeak-dev <[hidden email]> im Auftrag von Stéphane Rollandin <[hidden email]>
Gesendet: Montag, 23. September 2019 20:56:57
An: [hidden email]
Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1540.mcz
 
Le 23/09/2019 à 14:44, [hidden email] a écrit :
> A new version of Morphic was added to project The Inbox:
> http://source.squeak.org/inbox/Morphic-ct.1540.mcz
>
> ==================== Summary ====================
>
> Name: Morphic-ct.1540
> Author: ct
> Time: 23 September 2019, 4:44:32.818523 pm
> UUID: 064f7940-90ea-1646-a6f5-6c0a82c06e91
> Ancestors: Morphic-mt.1539
>
> Proposal: Remember & explore provenance of any morph.

-1

Please do not add any kludge to Morphs - they are already slow and fat.
Morphs are the basic components of any morphic thing, we should be able
to handle *a lot* of them with the less possible overhead.

Stef




Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Stéphane Rollandin
> Do you also refer to Marcel's proposal, which I implemented in
> Morphic-ct.1541? This does not affect normal Morphic use at all, unless
> you use explicitly call #rememberProvenanceDuring:.

Well I missed that one... give me some time to see what it is :)

Stef



Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Christoph Thiede

Hi Stéphane, hi all!


Was that enough time for seeing what it's worth? ;-) I use the #rememberProvenance utility from time to time and cannot see any harm, because when disabled as by default, the total overhead makes up tiny 12 byte code steps (for comparison, the bounds initialization costs 13 steps). If you still feel worried about this, we could also lower the overhead to 2 steps by inlining the class variable access. I'd love to see this in Trunk soon, IMHO it also makes the new Objectland example projects which will be ready soon much better explorable.


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Stéphane Rollandin <[hidden email]>
Gesendet: Montag, 23. September 2019 21:01:56
An: [hidden email]
Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1540.mcz
 
> Do you also refer to Marcel's proposal, which I implemented in
> Morphic-ct.1541? This does not affect normal Morphic use at all, unless
> you use explicitly call #rememberProvenanceDuring:.

Well I missed that one... give me some time to see what it is :)

Stef





Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Stéphane Rollandin
> Hi Stéphane, hi all!
>
>
> Was that enough time for seeing what it's worth? ;-)

I guess so :) Go on!

Stef


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Morphic-ct.1540.mcz

Christoph Thiede

Great! But it's not up to me to go ahead, someone else would need to review + merge this into the Trunk! :-)


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Stéphane Rollandin <[hidden email]>
Gesendet: Freitag, 18. September 2020 18:56:00
An: [hidden email]
Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1540.mcz
 
> Hi Stéphane, hi all!
>
>
> Was that enough time for seeing what it's worth? ;-)

I guess so :) Go on!

Stef