<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css" style="display:none;"><!-- P {margin-top:0;margin-bottom:0;} --></style>
</head>
<body dir="ltr">
<div id="divtagdefaultwrapper" dir="ltr" style="font-size: 12pt; color: rgb(0, 0, 0); font-family: Calibri, Helvetica, sans-serif, EmojiFont, "Apple Color Emoji", "Segoe UI Emoji", NotoColorEmoji, "Segoe UI Symbol", "Android Emoji", EmojiSymbols;">
<div id="divtagdefaultwrapper" dir="ltr" style="font-size:12pt; color:rgb(0,0,0); font-family:Calibri,Helvetica,sans-serif,EmojiFont,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols">
<p>Thanks for your thoughts!</p>
<p><br>
</p>
<p>Well, I already did some benchmarks, but I find it hard to achieve objective results:</p>
<p><br>
</p>
</div>
<blockquote style="margin:0 0 0 40px; border:none; padding:0px">
<div dir="ltr" style="font-size:12pt; color:rgb(0,0,0); font-family:Calibri,Helvetica,sans-serif,EmojiFont,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols">
<p></p>
<div><span style="font-size:10pt">cleanUp := [</span></div>
<div><span style="white-space:pre; font-size:10pt"></span><span style="font-size:10pt">    TransformationMorph allSubInstancesDo: [:m | m submorphs ifEmpty: [m delete]].</span></div>
<div><span style="white-space:pre; font-size:10pt"></span><span style="font-size:10pt">    5 timesRepeat: [Smalltalk garbageCollect]].</span></div>
<div><br>
</div>
<div><br>
</div>
<div><span style="font-size:10pt">Morph rememberProvenance: false.</span></div>
<div><span style="font-size:10pt">cleanUp value.</span></div>
<div><span style="font-size:10pt">[TransformationMorph new] bench." '526,000 per second. 1.9 microseconds per run.'"</span></div>
<div><br>
</div>
<div>"Test 2"</div>
<div><span style="font-size:10pt">Morph rememberProvenance: true.</span></div>
<div><span style="font-size:10pt">cleanUp value.</span></div>
<div><span style="font-size:10pt">[TransformationMorph new] bench." '118,000 per second. 8.44 microseconds per run.'"</span></div>
<div><br>
</div>
<div><br>
</div>
<div><span style="font-size:10pt">Morph rememberProvenance: false.</span></div>
<div><span style="font-size:10pt">cleanUp value.</span></div>
<div><span style="font-size:10pt">[TransformationMorph new openInWorld] bench." '3,790 per second. 264 microseconds per run.'"</span></div>
<div><br>
</div>
<div><span style="font-size:10pt">Morph rememberProvenance: true.</span></div>
<div><span style="font-size:10pt">cleanUp value.</span></div>
<div><span style="font-size:10pt">[TransformationMorph new openInWorld] bench." '3,690 per second. 271 microseconds per run.'"</span></div>
<p></p>
</div>
</blockquote>
<div dir="ltr" style="font-size:12pt; color:rgb(0,0,0); font-family:Calibri,Helvetica,sans-serif,EmojiFont,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols">
<p><br>
</p>
<p>Could we say: the overhead nearly disappears when compared to the costs of actually opening a morph?</p>
<p>How could I measure the memory load appropriately?</p>
<p><br>
</p>
<p></p>
<div>> -1 to keeping track of the #creatorStack because compiled methods can outdate in a long-running image</div>
<div><br>
</div>
<div>See <span>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).</span></div>
<br>
<p></p>
<p>> <span>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?</span></p>
<p><span><br>
</span></p>
<p><span>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.</span></p>
<p><span><br>
</span></p>
<p><span>> <span>Morph keepTrackOfCreationDuring: someBlock.</span></span></p>
<p><span><span>+1 :-)</span></span></p>
<p><span><span>So you mean all I need to update is the class-side of Morph?</span></span></p>
<p><span><span><br>
</span></span></p>
<p><span><span>Best,</span></span></p>
<p><span><span>Christoph</span></span></p>
<br>
<div style="color:rgb(0,0,0)">
<hr tabindex="-1" style="display:inline-block; width:98%">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" color="#000000" style="font-size:11pt"><b>Von:</b> Squeak-dev <squeak-dev-bounces@lists.squeakfoundation.org> im Auftrag von Taeumel, Marcel<br>
<b>Gesendet:</b> Montag, 23. September 2019 17:12 Uhr<br>
<b>An:</b> Alan Grimes via Squeak-dev<br>
<b>Betreff:</b> Re: [squeak-dev] The Inbox: Morphic-ct.1540.mcz</font>
<div> </div>
</div>
<div>
<div id="__MailbirdStyleContent" style="font-size:10pt; font-family:Arial; color:#000000">
<div>Hmm... this feature has quite an impact on Morph creation time and the overall memory footprint, right? Did you benchmark that somehow?</div>
<div><br>
</div>
<div>From a first impression, I would say:</div>
<div><br>
</div>
<div>-1 to keeping track of the #<span style="font-family:Arial,Helvetica,sans-serif; font-size:13.3333px">creatorStack because compiled methods can outdate in a long-running image</span></div>
<div>+1 to keeping track of the #<span style="font-family:Arial,Helvetica,sans-serif; font-size:13.3333px">officialCreator because that could help setting a breakpoint at the right place</span></div>
<div><span style="font-family:Arial,Helvetica,sans-serif; font-size:13.3333px"><br>
</span></div>
<div><span style="font-family:Arial,Helvetica,sans-serif; font-size:13.3333px">Still ... w</span><span style="font-size:10pt">hat 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?</span></div>
<div><br>
</div>
<div>Maybe there could be a way to keep track of morph creation explicitely?</div>
<div><br>
</div>
<div>Morph keepTrackOfCreationDuring: someBlock.</div>
<div><br>
</div>
<div>Even with a preference, adding such a thing to Morph >> #initialize requires actual benchmarks to asses the performance and memory overhead.</div>
<div><br>
</div>
<div>Just my two cents. :-)</div>
<div><br>
</div>
<div>Best,</div>
<div>Marcel</div>
<div class="mb_sig"></div>
<blockquote class="history_container" type="cite" style="border-left-style:solid; border-width:1px; margin-top:20px; margin-left:0px; padding-left:10px; min-width:500px">
<p style="color:#AAAAAA; margin-top:10px">Am 23.09.2019 16:47:29 schrieb Thiede, Christoph <christoph.thiede@student.hpi.uni-potsdam.de>:</p>
<div style="font-family:Arial,Helvetica,sans-serif">
<meta content="text/html; charset=UTF-8">
<div dir="ltr">
<div id="x_divtagdefaultwrapper" dir="ltr" style="font-size:12pt; color:rgb(0,0,0); font-family:Calibri,Helvetica,sans-serif,EmojiFont,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols">
<p>How do you like that?</p>
<p><img size="29554" id="x_img206000" tabindex="0" style="max-width: 99.9%; user-select: none;" src="cid:b635a64a-7981-444c-af79-70188915420b"><br>
</p>
<p>Can you notice a real performance impact when activating the preference? What is on Raspi?</p>
<p><br>
</p>
<p>Best,</p>
<p>Christoph</p>
<div id="x_Signature">
<div name="x_divtagdefaultwrapper" style="font-family:Calibri,Arial,Helvetica,sans-serif; font-size:; margin:0">
<div><span style="font-size:10pt; color:#808080"></span></div>
</div>
</div>
</div>
<hr tabindex="-1" style="display:inline-block; width:98%">
<div id="x_divRplyFwdMsg" dir="ltr"><span style="font-family:Calibri,sans-serif; color:#000000"><b>Von:</b> Squeak-dev <squeak-dev-bounces@lists.squeakfoundation.org> im Auftrag von commits@source.squeak.org <commits@source.squeak.org><br>
<b>Gesendet:</b> Montag, 23. September 2019 16:44:39<br>
<b>An:</b> squeak-dev@lists.squeakfoundation.org<br>
<b>Betreff:</b> [squeak-dev] The Inbox: Morphic-ct.1540.mcz</span>
<div> </div>
</div>
</div>
<span style="font-size:10pt"><span style="font-size:10pt">
<div class="PlainText">A new version of Morphic was added to project The Inbox:<br>
<a href="http://source.squeak.org/inbox/Morphic-ct.1540.mcz" id="LPlnk840316" previewremoved="true">http://source.squeak.org/inbox/Morphic-ct.1540.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: Morphic-ct.1540<br>
Author: ct<br>
Time: 23 September 2019, 4:44:32.818523 pm<br>
UUID: 064f7940-90ea-1646-a6f5-6c0a82c06e91<br>
Ancestors: Morphic-mt.1539<br>
<br>
Proposal: Remember & explore provenance of any morph.<br>
<br>
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.<br>
<br>
=============== Diff against Morphic-mt.1539 ===============<br>
<br>
Item was changed:<br>
  Object subclass: #Morph<br>
         instanceVariableNames: 'bounds owner submorphs fullBounds color extension'<br>
+        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius RememberProvenance UseSoftDropShadow'<br>
-        classVariableNames: 'HaloForAll IndicateKeyboardFocus MetaMenuForAll PreferredCornerRadius UseSoftDropShadow'<br>
         poolDictionaries: ''<br>
         category: 'Morphic-Kernel'!<br>
  <br>
  !Morph commentStamp: 'klc 3/14/2017 11:30' prior: 0!<br>
  A Morph (from the Greek "shape" or "form") is an interactive graphical object. General information on the Morphic system can be found at
<a href="http://wiki.squeak.org/squeak/30" id="LPlnk932468" previewremoved="true">
http://wiki.squeak.org/squeak/30</a>. <br>
  <br>
  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. <br>
  <br>
  The World (screen) coordinate system is used for most coordinates, but can be changed if there is a TransformMorph somewhere in the owner chain.
<br>
  <br>
  My instance variables have accessor methods (e.g., #bounds, #bounds:). Most users should use the accessor methods instead of using the instance variables directly.<br>
  <br>
  Structure:<br>
  instance var   Type                     Description <br>
  bounds                         Rectangle                A Rectangle indicating my position and a size that will enclose                                                                  me.
<br>
  owner                  Morph             My parent Morph, or nil for the top-level Morph, which is a<br>
                                 or nil                  world, typically a PasteUpMorph.<br>
  submorphs              Array                    My child Morphs. <br>
  fullBounds             Rectangle                A Rectangle minimally enclosing me and my submorphs.
<br>
  color                  Color                    My primary color. Subclasses can use this in different ways.
<br>
  extension              MorphExtension Allows extra properties to be stored without adding a<br>
                                 or nil                           storage burden to all morphs.
<br>
  <br>
  By default, Morphs do not position their submorphs. Morphs may position their submorphs directly or use a LayoutPolicy to automatically control their submorph positioning.<br>
  <br>
  Although Morph has some support for BorderStyle, most users should use BorderedMorph if they want borders.!<br>
<br>
Item was added:<br>
+ ----- Method: Morph class>>rememberProvenance (in category 'preferences') -----<br>
+ rememberProvenance<br>
+ <br>
+        <preference: 'Remember provenance of each morph'<br>
+                categoryList: #(Morphic)<br>
+                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.'<br>
+                type: #Boolean><br>
+        ^ RememberProvenance ifNil: [false]!<br>
<br>
Item was added:<br>
+ ----- Method: Morph class>>rememberProvenance: (in category 'preferences') -----<br>
+ rememberProvenance: aBoolean<br>
+ <br>
+        RememberProvenance := aBoolean.!<br>
<br>
Item was added:<br>
+ ----- Method: Morph>>browseCreatorMethod (in category 'debug and other') -----<br>
+ browseCreatorMethod<br>
+ <br>
+        ^ (self valueOfProperty: #officialCreator) browse!<br>
<br>
Item was changed:<br>
  ----- Method: Morph>>buildDebugMenu: (in category 'debug and other') -----<br>
  buildDebugMenu: aHand<br>
         "Answer a debugging menu for the receiver.  The hand argument is seemingly historical and plays no role presently"<br>
  <br>
         | aMenu aPlayer |<br>
         aMenu := MenuMorph new defaultTarget: self.<br>
         aMenu addStayUpItem.<br>
         (self hasProperty: #errorOnDraw) ifTrue:<br>
                 [aMenu add: 'start drawing again' translated action: #resumeAfterDrawError.<br>
                 aMenu addLine].<br>
         (self hasProperty: #errorOnStep) ifTrue:<br>
                 [aMenu add: 'start stepping again' translated action: #resumeAfterStepError.<br>
                 aMenu addLine].<br>
  <br>
         aMenu add: 'inspect morph' translated action: #inspectInMorphic:.<br>
         aMenu add: 'inspect owner chain' translated action: #inspectOwnerChain.<br>
         Smalltalk isMorphic ifFalse:<br>
                 [aMenu add: 'inspect morph (in MVC)' translated action: #inspect].<br>
  <br>
         self isMorphicModel ifTrue:<br>
                 [aMenu add: 'inspect model' translated target: self model action: #inspect;<br>
                         add: 'explore model' translated target: self model action: #explore].<br>
         (aPlayer := self player) ifNotNil:<br>
                 [aMenu add: 'inspect player' translated target: aPlayer action: #inspect].<br>
  <br>
       aMenu add: 'explore morph' translated target: self selector: #exploreInMorphic:.<br>
  <br>
         aMenu addLine.<br>
         aPlayer ifNotNil:<br>
                 [ aMenu add: 'viewer for Player' translated target: self player action: #beViewed.<br>
         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 ].<br>
  <br>
         aMenu add: 'viewer for Morph' translated target: self action: #viewMorphDirectly.<br>
         aMenu balloonTextForLastItem: 'Opens a Viewer on this Morph, rather than on its Player' translated.<br>
         aMenu addLine.<br>
  <br>
         aPlayer ifNotNil:<br>
                 [aPlayer class isUniClass ifTrue: [<br>
                         aMenu add: 'browse player class' translated target: aPlayer selector: #haveFullProtocolBrowsedShowingSelector: argumentList: #(nil)]].<br>
         aMenu add: 'browse morph class' translated target: self selector: #browseHierarchy.<br>
+        self isMorphicModel<br>
-        (self isMorphicModel)<br>
                 ifTrue: [aMenu<br>
                                 add: 'browse model class'<br>
                                 target: self model<br>
                                 selector: #browseHierarchy].<br>
+        (self valueOfProperty: #officialCreator ifAbsent: [nil]) ifNotNil: [<br>
+                aMenu add: 'browse creator method' action: #browseCreatorMethod].<br>
+        (self hasProperty: #creatorStack) ifTrue: [<br>
+                aMenu add: 'explore creator stack' action: #exploreCreatorStack].<br>
         aMenu addLine.<br>
  <br>
         self addViewingItemsTo: aMenu.<br>
         aMenu <br>
                 add: 'make own subclass' translated action: #subclassMorph;<br>
                 add: 'save morph in file' translated  action: #saveOnFile;<br>
                 addLine;<br>
                 add: 'call #tempCommand' translated action: #tempCommand;<br>
                 add: 'define #tempCommand' translated action: #defineTempCommand;<br>
                 addLine;<br>
  <br>
                 add: 'control-menu...' translated target: self selector: #invokeMetaMenu:;<br>
                 add: 'edit balloon help' translated action: #editBalloonHelpText.<br>
  <br>
         ^ aMenu!<br>
<br>
Item was added:<br>
+ ----- Method: Morph>>exploreCreatorStack (in category 'debug and other') -----<br>
+ exploreCreatorStack<br>
+ <br>
+        ^ (self valueOfProperty: #creatorStack) explore!<br>
<br>
Item was changed:<br>
  ----- Method: Morph>>initialize (in category 'initialization') -----<br>
  initialize<br>
         "initialize the state of the receiver"<br>
         owner := nil.<br>
         submorphs := Array empty.<br>
         bounds := self defaultBounds.<br>
+        color := self defaultColor.<br>
+        self class rememberProvenance ifTrue: [<br>
+                | creatorStack |<br>
+                creatorStack := thisContext home stack collect: #method.<br>
+                self setProperty: #creatorStack toValue: creatorStack.<br>
+                creatorStack<br>
+                        detect: [:method | method hasPragma: #example]<br>
+                        ifFound: [:method | self setProperty: #officialCreator toValue: method]].!<br>
-        color := self defaultColor!<br>
<br>
<br>
</div>
</span></span></div>
</blockquote>
</div>
</div>
</div>
</div>
</div>
</body>
</html>