<?xml version="1.0" encoding="UTF-8"?>
<TEI xml:space="preserve" xmlns="http://www.tei-c.org/ns/1.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.tei-c.org/ns/1.0 https://raw.githubusercontent.com/kermitt2/grobid/master/grobid-home/schemas/xsd/Grobid.xsd"
 xmlns:xlink="http://www.w3.org/1999/xlink">
	<teiHeader xml:lang="en">
		<fileDesc>
			<titleStmt>
				<title level="a" type="main">MethodProxies: A Safe and Fast Message-Passing Control Library</title>
			</titleStmt>
			<publicationStmt>
				<publisher/>
				<availability status="unknown"><licence/></availability>
			</publicationStmt>
			<sourceDesc>
				<biblStruct>
					<analytic>
						<author>
							<persName><forename type="first">Sebastian</forename><forename type="middle">Jordan</forename><surname>Montaño</surname></persName>
							<email>sebastian.jordan@inria.fr</email>
							<affiliation key="aff0">
								<orgName type="laboratory">UMR 9189 CRIStAL</orgName>
								<orgName type="institution" key="instit1">Univ. Lille</orgName>
								<orgName type="institution" key="instit2">Inria</orgName>
								<orgName type="institution" key="instit3">CNRS</orgName>
								<orgName type="institution" key="instit4">Centrale Lille</orgName>
								<address>
									<addrLine>Park Plaza, Parc scientifique de la Haute-Borne, 40 Av. Halley Bât A</addrLine>
									<postCode>59650</postCode>
									<settlement>Villeneuve-d&apos;Ascq</settlement>
									<country key="FR">France</country>
								</address>
							</affiliation>
						</author>
						<author>
							<persName><forename type="first">Juan</forename><forename type="middle">Pablo</forename><surname>Sandoval Alcocer</surname></persName>
							<affiliation key="aff1">
								<orgName type="department" key="dep1">Department of Computer Science</orgName>
								<orgName type="department" key="dep2">School of Engineering</orgName>
								<orgName type="institution">Pontificia Universidad Católica de Chile</orgName>
								<address>
									<settlement>Santiago</settlement>
									<country key="CL">Chile</country>
								</address>
							</affiliation>
						</author>
						<author>
							<persName><forename type="first">Guillermo</forename><surname>Polito</surname></persName>
							<email>guillermo.polito@inria.fr</email>
							<affiliation key="aff0">
								<orgName type="laboratory">UMR 9189 CRIStAL</orgName>
								<orgName type="institution" key="instit1">Univ. Lille</orgName>
								<orgName type="institution" key="instit2">Inria</orgName>
								<orgName type="institution" key="instit3">CNRS</orgName>
								<orgName type="institution" key="instit4">Centrale Lille</orgName>
								<address>
									<addrLine>Park Plaza, Parc scientifique de la Haute-Borne, 40 Av. Halley Bât A</addrLine>
									<postCode>59650</postCode>
									<settlement>Villeneuve-d&apos;Ascq</settlement>
									<country key="FR">France</country>
								</address>
							</affiliation>
						</author>
						<author>
							<persName><forename type="first">Stéphane</forename><surname>Ducasse</surname></persName>
							<email>stephane.ducasse@inria.fr</email>
							<affiliation key="aff0">
								<orgName type="laboratory">UMR 9189 CRIStAL</orgName>
								<orgName type="institution" key="instit1">Univ. Lille</orgName>
								<orgName type="institution" key="instit2">Inria</orgName>
								<orgName type="institution" key="instit3">CNRS</orgName>
								<orgName type="institution" key="instit4">Centrale Lille</orgName>
								<address>
									<addrLine>Park Plaza, Parc scientifique de la Haute-Borne, 40 Av. Halley Bât A</addrLine>
									<postCode>59650</postCode>
									<settlement>Villeneuve-d&apos;Ascq</settlement>
									<country key="FR">France</country>
								</address>
							</affiliation>
						</author>
						<author>
							<persName><forename type="first">Pablo</forename><surname>Tesone</surname></persName>
							<email>pablo.tesone@inria.fr</email>
							<affiliation key="aff0">
								<orgName type="laboratory">UMR 9189 CRIStAL</orgName>
								<orgName type="institution" key="instit1">Univ. Lille</orgName>
								<orgName type="institution" key="instit2">Inria</orgName>
								<orgName type="institution" key="instit3">CNRS</orgName>
								<orgName type="institution" key="instit4">Centrale Lille</orgName>
								<address>
									<addrLine>Park Plaza, Parc scientifique de la Haute-Borne, 40 Av. Halley Bât A</addrLine>
									<postCode>59650</postCode>
									<settlement>Villeneuve-d&apos;Ascq</settlement>
									<country key="FR">France</country>
								</address>
							</affiliation>
						</author>
						<title level="a" type="main">MethodProxies: A Safe and Fast Message-Passing Control Library</title>
					</analytic>
					<monogr>
						<idno type="ISSN">1613-0073</idno>
					</monogr>
					<idno type="MD5">CB2A0EB9756CB6604E0B1D57DA56A356</idno>
				</biblStruct>
			</sourceDesc>
		</fileDesc>
		<encodingDesc>
			<appInfo>
				<application version="0.7.2" ident="GROBID" when="2025-04-23T18:10+0000">
					<desc>GROBID - A machine learning software for extracting information from scholarly documents</desc>
					<ref target="https://github.com/kermitt2/grobid"/>
				</application>
			</appInfo>
		</encodingDesc>
		<profileDesc>
			<textClass>
				<keywords>
					<term>instrumentation</term>
					<term>message-passing control</term>
					<term>error handling</term>
					<term>method compilation</term>
				</keywords>
			</textClass>
			<abstract>
<div xmlns="http://www.tei-c.org/ns/1.0"><p>The injection of monitoring code allows for real-time observation of the program, which has proven instrumental in developing tools that assist developers with various programming tasks. In dynamic languages such as Pharo, renowned for their rich meta-programming capabilities and dynamic method dispatch, such monitoring capabilities are particularly valuable. Message-passing control techniques are commonly used to monitor program execution at the method level, involving the execution of specific code before and after each method invocation. Implementing message-passing control techniques, however, poses many challenges, notably in terms of instrumentation overhead. Additionally, it is crucial for the message-passing mechanism to be safe: i.e., to accommodate recursive and reflective scenarios to ensure that it does not alter the execution of the monitored program, which could potentially lead to infinite loops or other unintended consequences.</p><p>Over the years, numerous techniques have been proposed to optimize message-passing control. This paper introduces MethodProxies, a message-passing instrumentation library that offers minimal overhead and is safe. We conduct a comparison between MethodProxies and two commonly used techniques implemented in the Pharo programming language: method substitution using the run:with:in:hook and source code modification. Our results demonstrate that MethodProxies offers significantly lower overhead compared to the other two techniques and is safe against infinite recursion.</p></div>
			</abstract>
		</profileDesc>
	</teiHeader>
	<text xml:lang="en">
		<body>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="1.">Introduction</head><p>In software development, a common challenge is understanding how a program behaves under various conditions during its execution. Without detailed insights, developers may struggle to pinpoint inefficiencies, identify bugs, or optimize performance. Code instrumentation serves as a solution to this problem by embedding additional code that allows for continuous tracking and analysis of program behavior in run time. This method not only aids in troubleshooting and refining software but also facilitates the creation of powerful development tools tailored to enhance overall software quality and functionality.</p><p>In pure object-oriented languages, such as Pharo or Smalltalk <ref type="bibr" target="#b0">[1]</ref>, objects communicate exclusively through message-passing: sending and receiving messages. Message-passing control involves managing this message-passing, typically by executing actions before or after a method's execution. These techniques are commonly utilized to monitor program execution at the method level, involving the execution of specific code before and after each method invocation <ref type="bibr" target="#b1">[2,</ref><ref type="bibr" target="#b2">3]</ref>. By integrating these control points, developers can seamlessly insert custom behaviors into the method execution cycle without altering the core logic of the methods themselves. Such techniques enable the collection and analysis of metrics that shed light on the application's execution flow and interactions, enhancing the understanding of the program's dynamic behavior.</p><p>The implementation of message-passing control techniques, however, introduces many challenges, particularly the overhead of instrumentation, which can potentially distort the collected data and massively degrade the application's performance. This creates a dilemma where the measurement process itself can impact the system behavior it aims to capture <ref type="bibr" target="#b3">[4]</ref>. Additionally, it is crucial for the message-passing mechanism to be safe: i.e., to accommodate recursive and reflective scenarios to ensure that it does not alter the execution of the monitored program, which could potentially lead to infinite loops or other unintended consequences <ref type="bibr" target="#b4">[5,</ref><ref type="bibr" target="#b5">6]</ref>.</p><p>Over the years, numerous techniques have been proposed to optimize message-passing control such as source code modification, specialization of error handling, and exploitation of the virtual machine lookup algorithm, among others <ref type="bibr" target="#b1">[2]</ref>. This paper introduces MethodProxies<ref type="foot" target="#foot_0">1</ref> , a message-passing control instrumentation library that offers minimal overhead and is safe. This minimal overhead ensures that the message-passing control technique remains practical to use. In addition, MethodProxies have been designed to be safe and avoid falling in endless loops when controlling system features. It is available under the MIT open-source license.</p><p>We conduct an empirical evaluation through benchmarks in the Pharo programming language. We compare MethodProxies with two commonly used techniques: method substitution using the run:with:in:hook and source code modification. We describe how these techniques function and the considerations they employ to ensure safe message-passing control. Our results demonstrate that MethodProxies offers significantly lower overhead when compared to the other two techniques.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="2.">Safe Message Passing Control</head><p>When users want to control a method's execution, they typically desire to execute an action both before and after the method's execution. These actions are traditionally designated as the beforeMethod and the afterMethod, respectively <ref type="bibr" target="#b6">[7,</ref><ref type="bibr" target="#b7">8,</ref><ref type="bibr" target="#b8">9]</ref>. A Meta-safe library designed for controlling message sending must meet the following requirements <ref type="bibr" target="#b4">[5,</ref><ref type="bibr" target="#b5">6]</ref>.</p><p>We will explain each of them in detail below.</p><p>• Meta-safe recursion. The library should incorporate a meta-safe recursion mechanism to prevent infinite recursions when the instrumentation calls an instrumented method.</p><p>• Thread safety. There is a possibility of multiple threads calling the same instrumented method concurrently. To address this scenario, the library must handle meta executions in a thread-safe manner.</p><p>• Safe handling of unwinding for exceptions and non-local returns. Instrumented methods may encounter exceptions or non-local returns. The library should ensure the execution of the afterMethod irrespective of whether the instrumented method encounters an exception or initiates a non-local return.</p><p>• Uninstrumentation. Pharo is a live programming environment. For this reason, we need to uninstrument the code after the analysis to maintain the system's integrity. The library should restore the original methods after instrumentation.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="2.1.">Meta-safe recursion</head><p>The library should incorporate a mechanism to safeguard against infinite recursion when instrumented methods recursively invoke each other <ref type="bibr" target="#b4">[5,</ref><ref type="bibr" target="#b5">6]</ref>. This situation arises when users define the beforeMethod or afterMethod hooks and inadvertently include calls to other instrumented methods within these definitions. Such recursive calls lead to infinite loops due to repeated invocation of instrumented methods by the instrumentation itself.</p><p>Consider the Listing 1: A user instruments the method AClass»foo, producing the instrumented version outlined below: AClass&gt;&gt;foo handler beforeMethod. "method code" handler afterMethod.</p><p>Handler&gt;&gt;before 'foo method called' logMessage. anInstanceOfAClass foo.</p><p>Listing 1: Meta-recursive call example</p><p>In the beforeMethod definition, the handler logs a message and subsequently calls the same instrumented method AClass»foo. As a result, every execution of the instrumented method triggers the before action, which again invokes AClass»foo, leading to an infinite meta-recursion.</p><p>Requirement. The library should provide a meta-safe recursion prevention mechanism to manage recursions originating from within the instrumentation code effectively.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="2.2.">Thread safety</head><p>There is a potential for multiple threads to invoke the same instrumented method concurrently. This scenario requires that the library handle meta-recursions appropriately, extending the consideration to multi-threaded environments. Consider this example: If one execution thread invokes the method AClass»foo, the beforeMethod will tag the execution as meta to prevent a recursive call to AClass»foowithin the same thread. However, if another thread concurrently calls AClass»foo, the original meta tag does not affect this new invocation.</p><p>Requirement. The library must manage meta-executions in a thread-specific manner. It should ensure that meta-executions are marked uniquely for each thread, allowing each thread's activities to be handled independently.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="2.3.">Safe handling of unwinding for exceptions and non-local returns</head><p>Instrumented methods may encounter exceptions or non-local returns, which can disrupt normal execution flow. A non-local return is a return instruction within a block closure that forces the return from the method where the block was defined. Non-local return instructions force an unwind of the call stack because it jumps over all the stack frames in between the block frame and its defining method frame. Non-local returns exist in programming languages such as Ruby, Scala, Kotlin, and Pharo, among others.</p><p>It is important for the library to guarantee the execution of the afterMethod regardless of whether the instrumented method encounters an exception or initiates a non-local return. Indeed, methods can experience non-local returns or raise exceptions that might abruptly terminate their execution, potentially preventing the afterMethod from being executed. Consider the example in Listing 2: A user instruments a method that includes a non-local return: </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>Listing 2: Non-local return</head><p>In this scenario, if the condition specified by ifAbsent: is met, the method will exit prematurely, and the code following, including the afterMethod, will not execute. Therefore, it is essential for the library to ensure the afterMethod is always executed, maintaining a consistent and reliable execution flow, irrespective of exceptions or non-local returns.</p><p>Requirement. The library must ensure that the afterMethod executes under all circumstances, whether an exception occurs or a non-local return is initiated.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="2.4.">Uninstrumentation</head><p>All Pharo applications and tools co-exist in the same run-time environment. This means that instrumenting some code can affect other parts of the system that are not under analysis in unintended ways. One way to ensure the system's integrity is to remove the instrumentation after an analysis has been performed.</p><p>Requirement. The library must uninstrument all the methods that were instrumented, restoring them to their original state.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="3.">Current message passing control techniques</head><p>In this section, we will discuss two commonly used instrumentation techniques that users can employ to control message passing in Pharo. Note that not all the solutions presented in <ref type="bibr" target="#b1">[2]</ref> are available today in Pharo.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="3.1.">Source code modification</head><p>A common instrumentation approach involves modifying the methods' source code to be instrumented. Given Pharo's fully reflective capabilities, users have the freedom to directly alter the source code of any method they wish to instrument <ref type="bibr" target="#b9">[10]</ref>. However, this method places significant responsibility on the users to manage potential issues such as meta-recursions that may arise during the instrumentation process.</p><p>For example, consider the method before and after a source code instrumentation in Listing 3: Listing 3: Code before and after instrumentation This implementation encapsulates the method to be instrumented within an ensure: block. This ensures that the afterMethod will be executed regardless of whether an exception occurs or a non-local return is initiated <ref type="bibr" target="#b10">[11]</ref>. Additionally, we encapsulate the before and after actions to prevent their execution in the event of a meta-call.</p><formula xml:id="formula_0">"</formula></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="3.2.">run:with:in: method hook</head><p>In Pharo, the methods of a class are stored within a method dictionary. This dictionary forms an association between the method selector and the corresponding instance of the CompiledMethod class. Notably, methods in Pharo are ordinary objects and are instances of this CompiledMethod class.</p><p>Each time a message is sent in the Pharo environment, the Virtual Machine (VM) performs a lookup to find the compiled method corresponding to the selector. Once located, the VM executes this method on the receiver, passing the necessary arguments. If the object found in the method dictionary is not an instance of the CompiledMethod class, indicating an exceptional scenario, the Pharo VM addresses this by sending the special message run:with:in:to the found object. The run:with:in:method receives the method's selector, the arguments, and the receiver as parameters, allowing any class to implement it and thus manage method execution within the Pharo environment. This functionality is available by default in the standard Pharo's VM.</p><p>The run:with:in:technique replaces a compiled method instance in the method dictionary with an object understanding a run:with:in:message, referred to here as ProxyObject. This method is similar to the substitution technique described in <ref type="bibr" target="#b1">[2]</ref>, but with a critical distinction: the substituting object is not confined to instances of CompiledMethod. Instead, it can be an instance of any class, greatly expanding the possibilities for method substitution beyond traditional constraints. Typically, the CompiledMethod is replaced by a ProxyObject that encapsulates and preserves the original method. When run:with:in:is triggered, this ProxyObject may first execute a before action, then execute the original method, followed by an after action. The following Listing 4 provides an example of a run:with:in:method of a ProxyObject: Note that in our previous example, considerations for meta recursion, multi-threading, and local returns are also essential. Upon uninstrumentation, the original method can be restored simply by replacing it back into the method dictionary of the class. This procedure ensures that the integrity and functionality of the original method are maintained, even after modification and subsequent restoration.</p><p>It is important to note that this approach also involves at least two additional lookup executions: one for finding the implementor of run:with:in:and another for the implementor of valueWithReceiver:arguments:. This technique is not optimal for the Just-In-Time (JIT) compiler, as it should have an intermediate routine to box the arguments and massage the calls. Additionally, it is not favorable for inline caches because the methods stored in the methods dictionary are not actual methods. As a result, this technique has a performance drawback.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="4.">MethodProxies</head><p>MethodProxies is a method-based instrumentation library written in Pharo inspired by MethodWrappers <ref type="bibr" target="#b2">[3]</ref> (see Section 7 for a comparison). It instruments Pharo code without specific virtual machine support. It permits the dynamic instrumentation of Pharo methods, enabling the execution of userdefined actions both before and after a method's execution. This functionality is achieved through two method hooks: beforeMethod and afterMethod, which users are required to implement. These hooks are invoked whenever an instrumented method is called.</p><p>Our new implementation differs from this original work by stratifying the proxies in two parts: the trap and the handler. This design is meant to prevent user mistakes. The low-level concerns such as the code instrumentation and patching are defined by the framework itself. Users only need to define the beforeMethod and afterMethod hooks in a handler object. In addition, MethodProxies is safe:</p><p>MethodProxies has a robust architecture that enables method proxying without encountering infinite loops. To mitigate meta-recursions -when a user calls an instrumented method within another instrumented method in the same execution thread-MethodProxies employs a mechanism to determine the current execution level: whether it is at the meta-level or the base level. If the execution is identified as being at the meta-level, the beforeMethod and afterMethod hooks are bypassed, allowing execution to proceed normally as if no instrumentation were installed.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="4.1.">MethodProxies in a nutshell</head><p>Figure <ref type="figure" target="#fig_2">1</ref> illustrates the process of instrumenting the method AClass»foo using MethodProxies. The upper part of the figure shows the uninstrumented code. The class AClass has a method foo which is indirectly referred by a caller.</p><p>In the bottom part, the figure shows how the code looks with the instrumentation. The caller, instead of activating the original method foo, activates a trapMethod instead. This trapMethod activates the beforeMethod, the original method foo, and the afterMethod, respectively. The method object of the selector #foo, is replaced by this trap method, and the original method is hidden under a hidden selector named __foo.</p><p>• MethodProxies puts the instance of the compiled method foo under a hidden selector within the AClass method dictionary.</p><p>• It selects a prototype method with the same number of arguments as the method intended for instrumentation. Using literal patching in the prototype method, it integrates a call to the original method foo via the hidden selector. Furthermore, the prototype method incorporates calls to the before and after actions, the meta-safe mechanism, and the method deactivators.</p><p>• The instance of the compiled method associated with the selector #foo is replaced with the instrumented prototype method. As a result, when AClass»foois called, it invokes the prototype method instead.</p><p>• During uninstrumentation, it restores the original method foo, which is hidden under the hidden selector.</p><p>MethodProxies offers a straightforward API that is simple to understand and use. Listing 5 presents a practical example demonstrating the API's usage:   </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="4.2.">The trap method</head><p>For MethodProxies, we introduced an alternative approach to implement the instrumentation: the trap method. During the instrumentation of the method, we copy a pre-compiled method template called the trap method. This trap method encapsulates the method intended for instrumentation alongside the beforeMethod and afterMethod hooks. Using literal patching, we modify the bytecode of our template trap method. Brant et al., <ref type="bibr" target="#b2">[3]</ref> use the same technique. It features a meta-safe mechanism to prevent the execution from entering into an infinite loop. Additionally, it incorporates deactivators that are triggered if the method intended for instrumentation raises an exception or has a non-local return. We hide the original method in the method dictionary under a hidden selector, to be able to restore it at a later stage. Listing 7: Hidden original source code Note that the after handler is not enclosed within an ensure block. This is unnecessary as we leverage the exception handling mechanism in Pharo to handle the eventual non-local returns and exceptions. Further details on this will be provided in the subsequent section.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="4.3.">Stack unwinding</head><p>A method may encounter non-local returns, causing the execution to jump to the frame where the non-local return was defined. This disrupts the flow of the trap method. If an exception is raised, it disrupts the execution in a similar manner.</p><p>To address this issue, one common approach is to utilize the ensure: method <ref type="bibr" target="#b11">[12,</ref><ref type="bibr" target="#b10">11]</ref>. The method ensure: expects a block as an argument and ensures its execution regardless of whether an exception or non-local return occurs. However, employing ensure: incurs a performance cost because it requires wrapping the code of the method in a block closure. This drawback leads us to opt for a different technique instead.</p><p>We introduced a technique that involves handling the stack unwinding within the trap method. This approach operates similarly to the ensure: method, but instead of encapsulating the code we want to ensure its execution, we embed the code directly within the trap method. To implement this, we annotate the method with a special primitive designed to always fail, thus marking it for stack unwinding. The first temporary variable is designated to store the unwind block that needs execution. Despite being labeled a primitive, this construct is not an actual primitive because it is intended to fail consistently. Its sole purpose is to indicate, during stack unwinding, the methods in which Pharo must execute the unwind block, mirroring the functionality of the ensure: method. Additionally, we implement a method deactivator, which is a specialized object responsible for executing the afterMethod if an exception is raised.</p><p>In employing this technique, we initially execute the code as usual, presuming no exceptions will be raised. If an exception or non-local return does occur, we activate the deactivators using the same exception handling mechanism as the ensure: method.</p><p>By marking the trap method with a marker primitive, Pharo's exception mechanism triggers the execution of our deactivators if an exception occurs. This technique mitigates the performance cost because no block closures are created. To achieve this optimization, we made changes to the way Pharo handles exceptions. These enhancements have been integrated into Pharo 12.</p><p>We will take the code snippet in Listing 6 as an example to explain the method deactivators. If, during the execution of the trap method installed in AClass»foo, an exception or a non-local return is encountered, then:</p><p>• Pharo's exception mechanism will treat the first temporary variable as the unwind block.</p><p>• Next, Pharo's exception mechanism will check the second temporary variable. If its value is not true, then it will execute the unwind block.</p><p>If there are no exceptions or non-local returns during execution, the deactivator will not be executed, and the execution will proceed normally.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="5.">Experimental setup</head><p>We designed an experiment to contrast MethodProxies against run:with:in:and source code modification techniques. Our goal is to understand the impact of MethodProxies in terms of instrumentation time, overhead, and uninstrumentation time. All the experimentation code is available online<ref type="foot" target="#foot_1">2</ref> .</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="5.1.">Research questions</head><p>This paper studies the following research questions regarding instrumentation techniques in Pharo:</p><p>• RQ1 -Instrumentation and uninstrumentation overhead: How does the instrumentation time of MethodProxies compare to the run:with:in:and the source code modification instrumentation techniques? This question aims to understand the impact of MethodProxies on instrumentation and uninstrumentation time.</p><p>• RQ2 -Execution overhead: How does the overhead of MethodProxies compare to the run:with:in:and the source code modification instrumentation techniques? This question aims to compare the overhead time of MethodProxies with that of run:with:in:and source code modification techniques. We aim to assess the impact of the improvements in terms of execution time overhead.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="5.2.">Projects under analysis</head><p>Table <ref type="table" target="#tab_2">1</ref> describes the four projects we used for our analysis. It also reports the number of methods to instrument and the number of tests. To execute these projects, we run all its associated tests. We define a benchmark as the execution of a project's test suites. </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="5.3.">Scenarios under study</head><p>Given that the overhead may vary with the type of information collected during execution, we have developed three analysis tools:</p><p>• Method call graph: This analysis tool instruments all methods within an application to generate a graph that illustrates the relationships and frequencies of method calls. The method call graph is constructed with method-level granularity, with each method call being counted as an execution.</p><p>It reports the frequency of method calls and identifies the callers. Additionally, the method call graph accommodates multi-threaded executions by distinguishing methods called from threads other than the executing one. Each time an instrumented method is called, the analysis updates the method call graph.</p><p>• Method coverage: This tool instruments all methods within an application to record which methods are invoked by a set of tests. It identifies methods that were invoked, as well as those that were not, during the test execution. The coverage tool operates at method granularity, considering a method executed if it is called during testing. Each time an instrumented method is called, the tool marks the method as executed in a table.</p><p>• No-action instrumentation: This tool involves instrumenting all methods within an application without executing any actions. It utilizes empty method bodies for both the beforeMethod and afterMethod hooks. This setup allows for the evaluation of the overhead associated with bare instrumentation.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="5.4.">Techniques under analysis</head><p>We employed three distinct instrumentation techniques-(i) MethodProxies, (ii) run:with:in:hook, and (iii) source code modification. Despite the variation in these techniques, we ensured uniform implementation for both the method call graph and method coverage across all approaches. Each analysis tool was implemented in a manner agnostic to the specific instrumentation techniques used. This uniform approach allows us to deploy the same analysis tools across all instrumentation techniques, such as MethodProxies, run:with:in:hook, and source code modification, thus introducing consistent overhead across techniques. Additionally, we applied the same meta-checking mechanism across all instrumentation techniques to maintain consistent meta-safety overhead among them.</p><p>It is important to note that the analysis tools themselves introduce varying levels of overhead. For instance, the method call graph tool requires additional calculations to determine the relationships between callers and callees, while the method coverage tool simply marks the executed methods. This variation in computational workload may lead to different overhead impacts for the same projects.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="5.5.">Benchmark execution and metrics</head><p>To ensure accurate measurement of execution times, we take the following considerations:</p><p>• We performed 30 VM iterations for each benchmark, with a single VM invocation per iteration, consistent with recommendations from prior studies <ref type="bibr" target="#b13">[14]</ref>. We utilized ReBench <ref type="bibr" target="#b14">[15]</ref> to manage our benchmark execution efficiently.</p><p>• The Pharo VM uses a non-optimizing baseline JIT compiler that compiles methods upon their second invocation and does not apply further optimizations. Thus, we contend that limiting our benchmarks to one iteration per VM invocation does not impact our results adversely.</p><p>• We calculated and reported both the mean and the standard deviation for each benchmark's measurements. To reduce system-related noise during benchmark executions, we terminated all non-essential OS applications and disabled the internet connection.</p><p>We consider the execution of all tests within each project as benchmarks for our measurements. For each analysis tool and project, we collected the following metrics:</p><p>• Overhead Time: This metric is calculated as the ratio between the execution time with instrumentation (I ) and the execution time without instrumentation (NI ).</p><formula xml:id="formula_1">𝑂𝑣𝑒𝑟ℎ𝑒𝑎𝑑 = 𝐼 / 𝑁 𝐼<label>(1)</label></formula><p>• Instrumentation overhead: This represents the duration required by the analysis tools to instrument all methods prior to executing the benchmarks. It is calculated as the ratio between the instrumentation time (insTime) and the lowest instrumentation execution time (lowInsTime).</p><p>𝐼𝑛𝑠𝑡𝑟𝑢𝑚𝑒𝑛𝑡𝑎𝑡𝑖𝑜𝑛 𝑂𝑣𝑒𝑟ℎ𝑒𝑎𝑑 = 𝑖𝑛𝑠𝑇 𝑖𝑚𝑒 / 𝑙𝑜𝑤𝐼𝑛𝑠𝑇 𝑖𝑚𝑒.</p><p>(2)</p><p>• Uninstrumentation overhead: This measures the time taken by the analysis tools to restore the original compiled methods in the class. Similarly, it is calculated as the ratio between the uninstrumentation time (uninsTime) and the lowest uninstrumentation execution time (lowUnin-sTime).</p><p>𝑈 𝑛𝑖𝑛𝑠𝑡𝑟𝑢𝑚𝑒𝑛𝑡𝑎𝑡𝑖𝑜𝑛 𝑂𝑣𝑒𝑟ℎ𝑒𝑎𝑑 = 𝑢𝑛𝑖𝑛𝑠𝑇 𝑖𝑚𝑒 / 𝑙𝑜𝑤𝑈 𝑛𝑖𝑛𝑠𝑇 𝑖𝑚𝑒.</p><p>(3)</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="6.">Results</head><p>In this section, we present the results of our experiments. For each execution time, we report the mean value with the standard deviation over 30 runs.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="6.1.">RQ1 -Instrumentation and uninstrumentation overhead</head><p>For the first research question, we analyze how much time instrumenting and uninstrumenting the methods takes. Table <ref type="table" target="#tab_3">2</ref> presents the results of the benchmarks for the instrumentation overhead. Results are presented relative to the shortest time: lower values indicate better performance. The run:with:in:technique exhibits the lowest instrumentation overhead, with the fastest time being 140 milliseconds for the Compression benchmark, which has 387 methods to be instrumented. The shortest execution time by scenario among all analysis tools is in bold.</p><p>The run:with:in:hook exhibits the least instrumentation overhead, ranging from 1.0 to 1.57 ×. This is expected since this technique involves replacing the instance of CompiledMethod with self.</p><p>The source code modification technique incurs the most overhead for instrumentation, ranging from 6.36 to 282.37 ×. This is due to the requirement of string concatenation for creating the new methods' source code and recompiling all methods The overhead introduced by MethodProxies ranges from 1.16 to 2.38 ×. MethodProxies takes an instance of a pre-compiled method and replaces its references. Table <ref type="table" target="#tab_5">3</ref> presents the benchmark results for the uninstrumentation overhead. The shortest execution is 140.0 ±0.0 milliseconds for the Compression benchmark. The largest uninstrumentation time for uninstrumenting all the methods is 280.0 ±0.0 milliseconds for the AST benchmark. We use the same uninstrumentation mechanisms across all different analysis tools: we restore the compiled method object into the method dictionary. We will also present the numbers relative to the shortest execution time. </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>RQ.1 How does the instrumentation time MethodProxies compare to other instrumentation techniques?</head><p>MethodProxies incurs an instrumentation overhead ranging from 1.16 to 2.38 × compared to the fastest time of run:with:in:. While MethodProxies has a higher overhead than run:with:in:, it is significantly lower than the source code modification technique. For uninstrumentation, MethodProxies exhibits the lowest overhead, with values ranging from 1.07 to 2.0 × across all analysis tools.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="6.2.">RQ2 -Execution overhead</head><p>For this research question, we first run the benchmarks without the instrumentation to calculate their baseline execution time. Then, we present the execution overhead relative to this baseline execution time.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="6.2.1.">Baseline execution time</head><p>To compare the overhead added by the instrumentation, we initially calculated the execution time of the benchmarks, which we call the baseline execution time. Table <ref type="table" target="#tab_7">4</ref> presents the results for the execution time of the benchmarks without instrumentation. FileSystem tests are the ones that take the longest to execute, while the Microdown tests are the fastest. </p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="6.2.2.">Execution overhead</head><p>Table <ref type="table" target="#tab_8">5</ref> illustrates the execution overhead relative to the baseline execution time. We highlighted in bold the shortest execution time across all instrumentation techniques. MethodProxies has the lowest overhead among all benchmarks and all analysis tools. For the no-action tool, aimed at analyzing the cost of bare instrumentation, MethodProxies exhibits overheads between 0.91 and 5.15 × compared to the baseline execution. Interestingly, in the no-action tool with the Microdown benchmark, the execution was faster with MethodProxies than without instrumentation (0.91 ×), and we did not investigate further this. On the contrary, run:with:in:demonstrates the highest execution overhead across all benchmarks and analysis tools. One of the reasons for this can be the additional lookup required by the VM to locate where run:with:in:is implemented, as well as its lack of compatibility with inline caches and the JIT compiler. Additionally, the AST benchmark displays a significant overhead across all analysis tools, which warrants further investigation.</p><p>The source code modification overheads are in the middle, between MethodProxies and run:with:in:. We inline the code of the beforeMethod and afterMethod but we wrap the afterMethod inside an ensure: block. RQ.2 How does the overhead of MethodProxies compare to the run:with:in:and the source code modification tools? Among all benchmarks and analysis tools, MethodProxies exhibits the lowest execution overhead.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="7.">Related Work</head><p>In this section, we present the related work on instrumentation techniques and message-passing control.</p><p>Analysis tools. Bergel et al., <ref type="bibr" target="#b15">[16,</ref><ref type="bibr" target="#b16">17]</ref> developed Spy, a profiling framework that allows developers to build custom profilers. Spy instruments the desired methods to execute actions before and after a method's execution. It uses the run:with:in:technique to instrument the code and control message passing. Spy has been used to build various tools, including method coverage tools <ref type="bibr" target="#b17">[18]</ref>, among others.</p><p>In our work, we study different instrumentation techniques, such as MethodProxies, run:with:in:, and source code modification, comparing the overhead of these techniques. Unlike Bergel et al., which focus on the tools built using these instrumentation techniques, our focus is on the instrumentation techniques themselves and their associated overhead.</p><p>MethodWrappers. Brant et al., <ref type="bibr" target="#b2">[3]</ref> introduced Method Wrappers, a mechanism for introducing new behavior to be executed before or after a method. The authors explore several implementations of wrappers in Smalltalk and compare their performance with various program analysis tools, making this work the most similar to ours.</p><p>In their approach, they replace the instance of the CompiledMethod with an instance of a Method-Wrapper. This new method includes the before action, the original method, and the after action, executing the original method using the valueWithReceiver:arguments: method. This technique does not add a new entry to the method dictionary and it does not hide the original method under a hidden selector. However, the MethodWrapper implementation lacks safety, requiring users to subclass MethodWrappers to handle the instrumentation themselves. This leaves the responsibility of managing safety mechanisms, such as meta-recursions, to the user.</p><p>In contrast, we implemented MethodProxies by replacing the instance of the compiled method with a pre-compiled trap method, which we then patch using literal patching. This approach is similar to MethodWrappers but offers a more robust and stratified architecture. With MethodProxies, users can define before and after methods without worrying about safety concerns such as meta-recursions. The user only needs to define the before and after methods, as the safety-ensuring mechanisms are handled automatically.</p><p>Sub-method Reflection. Reflectivity <ref type="bibr" target="#b18">[19,</ref><ref type="bibr" target="#b19">20,</ref><ref type="bibr" target="#b20">21,</ref><ref type="bibr" target="#b21">22]</ref> is a framework that allows developers to annotate abstract syntax tree (AST) nodes with meta-behavior, influencing the compiler to produce behavioral variations. These annotations are dynamically applied to AST nodes, which are then expanded, compiled, and executed. Notably, in Pharo, the AST is accessible at the language level, facilitating its modification. Reflectivity provides the essential infrastructure to support these capabilities. The front end of Reflectivity is designed to operate at AST level. We excluded Reflectivity from our comparison because is conceptually equivalent to the source code modification, as it needs to recompile the method with the AST modifications.</p><p>Infinite meta-recursions. Denker et al., <ref type="bibr" target="#b5">[6]</ref> worked on the problem of infinite meta-recursions in reflective languages. Mainstream languages use a reflective architecture to enable reflection. In this architecture, meta-objects control the different aspects of reflection offered by the language. The authors extended the meta-object-based reflective systems with a first-class notion of meta-level execution and the possibility to query at any point in the execution whether we are executing at the meta-level or not.</p><p>In CLOS, Kiczales et al., <ref type="bibr" target="#b22">[23]</ref> introduced an approach to programming language design, focusing on the evolution and principles of the Common Lisp Object System (CLOS) metaobject protocol. The work emphasizes that metaobject protocols enable users to customize programming languages to better meet their needs. The authors used memoization to speed up method lookup and dispatch.</p><p>Chiba et al., <ref type="bibr" target="#b4">[5]</ref> presented a new architecture, called the meta-helix, for systems that use the metaobject protocol. A common element of meta-object protocol design is the use of metacircularity to allow extensions to be implemented in terms of the original non-extended functionality. However, this design can lead to recursion due to the conflation of the extended and non-extended functionalities. Meta-helix architecture retains the benefits of metacircularity while addressing its problems.</p><p>We used these definitions of infinite meta-recursions as the foundation for building MethodProxies.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="8.">Discussion and future work</head><p>In this section, we will discuss the threats to validity and outline our future work.</p><p>Pharo's unsafe threads In Pharo, it is possible to terminate a thread at any point during its execution, even if the thread is being executed in a critical section. This capability contrasts with many mainstream languages, such as Java<ref type="foot" target="#foot_2">3</ref> , which do not allow thread termination. This feature in Pharo can lead to significant issues. For instance, if a thread is executing code that marks the execution as being at the meta-level and another thread terminates it, the execution state will become inconsistent or corrupted. This situation falls outside the control of MethodProxies, as it is inherent to Pharo's threading model.</p><p>Special methods used by the instrumentation. MethodProxies employs some special methods to instrument the code. These special methods cannot be instrumented, as they are essential for the instrumentation process. prevent their instrumentation, we mark these methods with the pragma &lt;noInstrumentation&gt;. These methods are specific to MethodProxies, so users typically will not need to instrument them.</p><p>Other instrumentation techniques. Our study compared MethodProxies against two commonly utilized message-passing control techniques: run:with:in:and source code modification. Although these techniques have provided valuable insights into instrumentation overheads, it is important to acknowledge the existence of other possible techniques outlined in the literature <ref type="bibr" target="#b1">[2]</ref> that were not included in this comparison. As part of our future work, we plan to explore and analyze additional instrumentation techniques to deliver a more comprehensive comparison and deepen our understanding of their associated overheads.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head>Benchmarks choice.</head><p>For our experiments, we opted to execute all tests within the selected benchmarks. While this approach ensures comprehensive coverage, it may not accurately represent these benchmarks' most typical use cases. In future research, we aim to select a set of benchmarks that do not rely solely on test execution, thereby providing a more representative evaluation of the instrumentation techniques.</p><p>Safe vs. unsafe profiling. Our experiments focused on comparing different techniques of safe message-passing control for developing analysis tools. However, we did not investigate the impact of the safe mechanism itself or the additional overhead it introduces. In some scenarios, users might not require the safe-checking mechanism. In future work, we plan to include a comparison of safe vs. unsafe profiling to gain a deeper understanding of the overhead introduced by the safe-checking mechanism.</p></div>
<div xmlns="http://www.tei-c.org/ns/1.0"><head n="9.">Conclusion</head><p>This paper introduces MethodProxies, a new, safe, and fast message-passing instrumentation library tailored for the Pharo programming language. We demonstrate how MethodProxies efficiently handles multithreading, meta-recursions, exceptions, and local return scenarios. Furthermore, we present an experiment in which MethodProxies was assessed alongside two widely used techniques: the run:with:in:hook and source code modification, across a variety of profiling scenarios and projects.</p><p>Our findings indicate that MethodProxies consistently exhibits the lowest execution overhead, significantly outperforming both the run:with:in:hook and source code modification in all evaluated benchmarks and analysis tools. By leveraging the exception mechanism in Pharo and avoiding block closures, MethodProxies delivers optimized performance. The development of MethodProxies marks a significant advancement over traditional messagepassing techniques. Looking ahead, future research will focus on further optimizations of MethodProxies, comparing it with additional instrumentation techniques, investigating various types of benchmarks, exploring the impact of safe vs. unsafe profiling, and extending its applicability to other dynamic programming languages and runtime environments.</p></div><figure xmlns="http://www.tei-c.org/ns/1.0" xml:id="fig_0"><head></head><label></label><figDesc>AClass&gt;&gt;foohandler before. condition ifAbsent: [ ^self ]. handler after.</figDesc></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" xml:id="fig_1"><head></head><label></label><figDesc>ProxyObject &gt;&gt; run: selector with: args in: aReceiver | v | self isMetaForActiveProcess ifFalse: [ self runInMetaLevel: [ #beforeHandler] ]. [ v := originalMethod valueWithReceiver: aReceiver arguments: args ] ensure: [ self isMetaForActiveProcess ifFalse: [ self runInMetaLevel: [ #afterHandler ] ] ] ^v Listing 4: ProxyObject implementation of run:with:in:</figDesc></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" xml:id="fig_2"><head>Figure 1 :</head><label>1</label><figDesc>Figure 1: MethodProxies in a nutshell.</figDesc></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" xml:id="fig_3"><head>Listing 5 :</head><label>5</label><figDesc>MethodProxies's API usage</figDesc></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" xml:id="fig_4"><head></head><label></label><figDesc>AClass&gt;&gt; foo: args "This is not a primitive, just a marker" &lt;primitive: 198&gt; "The unwind handler should be the first temp. The complete flag should be the second temp." | deactivator complete result | deactivator := #deactivator. #beforeHandler. result := self ___foo: args. #afterHandler. "Mark the execution as complete to avoid double execution of the unwind handler" complete := true. ^result Listing 6: Trap method AClass&gt;&gt; ___foo: args "Original source code hidden under the hidden selector"</figDesc></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_2"><head>Table 1</head><label>1</label><figDesc>Projects under analysis</figDesc><table><row><cell>Project's name</cell><cell>Description</cell></row></table></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_3"><head>Table 2</head><label>2</label><figDesc>Instrumentation overhead in milliseconds</figDesc><table /></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_4"><head>MethodProxies run:with:in: Source code modification No-action tool</head><label></label><figDesc></figDesc><table><row><cell>FileSystem</cell><cell>2.13 × ±0.03</cell><cell>1.43 × ±0.01</cell><cell>20.78 × ±0.07</cell></row><row><cell>Microdown</cell><cell>1.64 × ±0.02</cell><cell>1.15 × ±0.01</cell><cell>12.46 × ±0.06</cell></row><row><cell cols="2">Compression 1.17 × ±0.03</cell><cell>1.0 × ±0.0</cell><cell>6.36 × ±0.0</cell></row><row><cell>AST</cell><cell>2.37 × ±0.03</cell><cell>1.53 × ±0.04</cell><cell>148.92 × ±2.02</cell></row><row><cell></cell><cell></cell><cell>Call graph tool</cell><cell></cell></row><row><cell>FileSystem</cell><cell>2.14 × ±0.0</cell><cell>1.43 × ±0.01</cell><cell>25.5 × ±0.12</cell></row><row><cell>Microdown</cell><cell>1.63 × ±0.02</cell><cell>1.16 × ±0.03</cell><cell>13.3 × ±0.08</cell></row><row><cell cols="2">Compression 1.17 × ±0.04</cell><cell>1.0 × ±0.0</cell><cell>6.65 × ±0.03</cell></row><row><cell>AST</cell><cell>2.38 × ±0.03</cell><cell>1.57 × ±0.0</cell><cell>282.37 × ±8.68</cell></row><row><cell></cell><cell cols="2">Method coverage tool</cell><cell></cell></row><row><cell>FileSystem</cell><cell>2.14 × ±0.0</cell><cell>1.43 × ±0.0</cell><cell>22.81 × ±0.1</cell></row><row><cell>Microdown</cell><cell>1.65 × ±0.01</cell><cell>1.16 × ±0.03</cell><cell>13.04 × ±0.08</cell></row><row><cell cols="2">Compression 1.16 × ±0.03</cell><cell>1.0 × ±0.0</cell><cell>6.57 × ±0.02</cell></row><row><cell>AST</cell><cell>2.36 × ±0.01</cell><cell>1.56 × ±0.02</cell><cell>199.28 × ±3.67</cell></row></table></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_5"><head>Table 3</head><label>3</label><figDesc>Uninstrumentation overhead in milliseconds</figDesc><table /></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_6"><head>MethodProxies run:with:in: Source code modification No-action tool</head><label></label><figDesc></figDesc><table><row><cell>FileSystem</cell><cell>1.78 × ±0.01</cell><cell>1.14 × ±0.0</cell><cell>1.65 × ±0.02</cell></row><row><cell>Microdown</cell><cell>1.17 × ±0.04</cell><cell>1.08 × ±0.02</cell><cell>1.3 × ±0.03</cell></row><row><cell cols="2">Compression 1.0 × ±0.0</cell><cell>1.07 × ±0.0</cell><cell>1.07 × ±0.0</cell></row><row><cell>AST</cell><cell>1.92 × ±0.02</cell><cell>1.21 × ±0.0</cell><cell>1.93 × ±0.01</cell></row><row><cell></cell><cell></cell><cell>Call graph tool</cell><cell></cell></row><row><cell>FileSystem</cell><cell>1.86 × ±0.02</cell><cell>1.21 × ±0.0</cell><cell>1.58 × ±0.02</cell></row><row><cell>Microdown</cell><cell>1.29 × ±0.0</cell><cell>1.21 × ±0.0</cell><cell>1.35 × ±0.02</cell></row><row><cell cols="2">Compression 1.07 × ±0.0</cell><cell>1.15 × ±0.01</cell><cell>1.14 × ±0.02</cell></row><row><cell>AST</cell><cell>2.0 × ±0.0</cell><cell>1.29 × ±0.02</cell><cell>1.92 × ±0.02</cell></row><row><cell></cell><cell cols="2">Method coverage tool</cell><cell></cell></row><row><cell>FileSystem</cell><cell>1.85 × ±0.01</cell><cell>1.21 × ±0.0</cell><cell>1.5 × ±0.0</cell></row><row><cell>Microdown</cell><cell>1.24 × ±0.03</cell><cell>1.15 × ±0.02</cell><cell>1.27 × ±0.03</cell></row><row><cell cols="2">Compression 1.07 × ±0.0</cell><cell>1.14 × ±0.0</cell><cell>1.12 × ±0.03</cell></row><row><cell>AST</cell><cell>1.94 × ±0.02</cell><cell>1.29 × ±0.0</cell><cell>1.79 × ±0.01</cell></row></table></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_7"><head>Table 4</head><label>4</label><figDesc>Baseline execution time in milliseconds (no instrumentation)</figDesc><table><row><cell cols="2">FileSystem Microdown Compression</cell><cell>AST</cell></row><row><cell>Execution time 10788 ±27.21 1330 ±128.3</cell><cell>2575 ±11.37</cell><cell>4733 ±44.73</cell></row></table></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_8"><head>Table 5</head><label>5</label><figDesc>Overhead for executing the instrumented code</figDesc><table /></figure>
<figure xmlns="http://www.tei-c.org/ns/1.0" type="table" xml:id="tab_9"><head>MethodProxies run:with:in: Source code modification No-action tool</head><label></label><figDesc></figDesc><table><row><cell>FileSystem</cell><cell>1.03 × ±0.0</cell><cell>1.17 × ±0.0</cell><cell>1.08 × ±0.0</cell></row><row><cell>Microdown</cell><cell>0.91 × ±0.1</cell><cell cols="2">17.98 × ±1.05 6.14 × ±0.16</cell></row><row><cell cols="2">Compression 1.05 × ±0.0</cell><cell>9.33 × ±0.22</cell><cell>3.31 × ±0.01</cell></row><row><cell>AST</cell><cell>5.15 × ±0.05</cell><cell cols="2">47.92 × ±2.75 23.33 × ±0.06</cell></row><row><cell></cell><cell></cell><cell>Call graph tool</cell><cell></cell></row><row><cell>FileSystem</cell><cell>1.07 × ±0.0</cell><cell>1.22 × ±0.0</cell><cell>1.11 × ±0.0</cell></row><row><cell>Microdown</cell><cell>4.35 × ±0.2</cell><cell cols="2">20.87 × ±1.13 8.7 × ±0.16</cell></row><row><cell cols="2">Compression 2.49 × ±0.01</cell><cell cols="2">10.56 × ±0.13 4.35 × ±0.01</cell></row><row><cell>AST</cell><cell>25.48 × ±0.23</cell><cell cols="2">49.87 × ±0.77 37.62 × ±0.15</cell></row><row><cell></cell><cell cols="2">Method coverage tool</cell><cell></cell></row><row><cell>FileSystem</cell><cell>1.05 × ±0.0</cell><cell>1.19 × ±0.0</cell><cell>1.09 × ±0.0</cell></row><row><cell>Microdown</cell><cell>2.22 × ±0.11</cell><cell>19.18 × ±0.8</cell><cell>6.82 × ±0.15</cell></row><row><cell cols="2">Compression 1.58 × ±0.01</cell><cell>9.73 × ±0.23</cell><cell>3.59 × ±0.01</cell></row><row><cell>AST</cell><cell>11.61 × ±0.13</cell><cell cols="2">44.08 × ±0.62 28.89 × ±0.09</cell></row></table></figure>
			<note xmlns="http://www.tei-c.org/ns/1.0" place="foot" n="1" xml:id="foot_0">https://github.com/pharo-contributions/MethodProxies</note>
			<note xmlns="http://www.tei-c.org/ns/1.0" place="foot" n="2" xml:id="foot_1">https://github.com/jordanmontt/pharo-instrumentation</note>
			<note xmlns="http://www.tei-c.org/ns/1.0" place="foot" n="3" xml:id="foot_2">https://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html</note>
		</body>
		<back>

			<div type="acknowledgement">
<div xmlns="http://www.tei-c.org/ns/1.0"><head>Acknowledgments</head><p>We want to thank the Programa de Inserción Académica 2022, Vicerrectoría Académica y Prorrectoría, at the Pontificia Universidad Católica de Chile. We also extend our gratitude to the European Smalltalk User Group (ESUG) for financing part of this work.</p></div>
			</div>

			<div type="references">

				<listBibl>

<biblStruct xml:id="b0">
	<monogr>
		<title level="m" type="main">Pharo 9 by Example, Book on Demand -Keepers of the lighthouse</title>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
		<author>
			<persName><forename type="first">G</forename><surname>Rakic</surname></persName>
		</author>
		<author>
			<persName><forename type="first">S</forename><surname>Kaplar</surname></persName>
		</author>
		<author>
			<persName><forename type="first">Q</forename><forename type="middle">D O</forename></persName>
		</author>
		<author>
			<persName><forename type="first">A</forename><surname>Black</surname></persName>
		</author>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
		<author>
			<persName><forename type="first">O</forename><surname>Nierstrasz</surname></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><forename type="middle">P</forename></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><surname>Cassou</surname></persName>
		</author>
		<author>
			<persName><forename type="first">M</forename><surname>Denker</surname></persName>
		</author>
		<ptr target="http://books.pharo.org" />
		<imprint>
			<date type="published" when="2022">2022</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b1">
	<analytic>
		<title level="a" type="main">Evaluating message passing control techniques in Smalltalk</title>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="j">Journal of Object-Oriented Programming</title>
		<imprint>
			<biblScope unit="volume">12</biblScope>
			<biblScope unit="page" from="39" to="44" />
			<date type="published" when="1999">1999</date>
		</imprint>
	</monogr>
	<note>JOOP)</note>
</biblStruct>

<biblStruct xml:id="b2">
	<analytic>
		<title level="a" type="main">Wrappers to the rescue</title>
		<author>
			<persName><forename type="first">J</forename><surname>Brant</surname></persName>
		</author>
		<author>
			<persName><forename type="first">B</forename><surname>Foote</surname></persName>
		</author>
		<author>
			<persName><forename type="first">R</forename><surname>Johnson</surname></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><surname>Roberts</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="m">Proceedings European Conference on Object Oriented Programming (ECOOP&apos;98)</title>
				<meeting>European Conference on Object Oriented Programming (ECOOP&apos;98)</meeting>
		<imprint>
			<publisher>Springer-Verlag</publisher>
			<date type="published" when="1998">1998</date>
			<biblScope unit="volume">1445</biblScope>
			<biblScope unit="page" from="396" to="417" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b3">
	<monogr>
		<title level="m" type="main">Observer effect and measurement bias in performance analysis</title>
		<author>
			<persName><forename type="first">T</forename><surname>Mytkowicz</surname></persName>
		</author>
		<author>
			<persName><forename type="first">P</forename><forename type="middle">F</forename><surname>Sweeney</surname></persName>
		</author>
		<author>
			<persName><forename type="first">M</forename><surname>Hauswirth</surname></persName>
		</author>
		<author>
			<persName><forename type="first">A</forename><surname>Diwan</surname></persName>
		</author>
		<idno>CU-CS-1042-08</idno>
		<ptr target="https://core.ac.uk/download/pdf/54846997.pdf" />
		<imprint>
			<date type="published" when="2008">2008</date>
		</imprint>
		<respStmt>
			<orgName>University of Colorado, Boulder</orgName>
		</respStmt>
	</monogr>
	<note type="report_type">Computer Science Technical Reports</note>
</biblStruct>

<biblStruct xml:id="b4">
	<analytic>
		<title level="a" type="main">Avoiding confusion in metacircularity: The meta-helix</title>
		<author>
			<persName><forename type="first">S</forename><surname>Chiba</surname></persName>
		</author>
		<author>
			<persName><forename type="first">G</forename><surname>Kiczales</surname></persName>
		</author>
		<author>
			<persName><forename type="first">J</forename><surname>Lamping</surname></persName>
		</author>
		<idno type="DOI">10.1007/3-540-60954-7_49</idno>
		<ptr target="10.1007/3-540-60954-7_49" />
	</analytic>
	<monogr>
		<title level="m">Proceedings of ISOTAS &apos;96</title>
				<editor>
			<persName><forename type="first">K</forename><surname>Futatsugi</surname></persName>
		</editor>
		<editor>
			<persName><forename type="first">S</forename><surname>Matsuoka</surname></persName>
		</editor>
		<meeting>ISOTAS &apos;96</meeting>
		<imprint>
			<publisher>Springer</publisher>
			<date type="published" when="1996">1996</date>
			<biblScope unit="volume">1049</biblScope>
			<biblScope unit="page" from="157" to="172" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b5">
	<analytic>
		<title level="a" type="main">The meta in meta-object architectures</title>
		<author>
			<persName><forename type="first">M</forename><surname>Denker</surname></persName>
		</author>
		<author>
			<persName><forename type="first">M</forename><surname>Suen</surname></persName>
		</author>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
		<idno type="DOI">10.1007/978-3-540-69824-1_13</idno>
	</analytic>
	<monogr>
		<title level="m">Proceedings of TOOLS EUROPE 2008</title>
				<meeting>TOOLS EUROPE 2008</meeting>
		<imprint>
			<publisher>Springer-Verlag</publisher>
			<date type="published" when="2008">2008</date>
			<biblScope unit="volume">11</biblScope>
			<biblScope unit="page" from="218" to="237" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b6">
	<analytic>
		<title level="a" type="main">Metalevel programming in CLOS</title>
		<author>
			<persName><forename type="first">G</forename><surname>Attardi</surname></persName>
		</author>
		<author>
			<persName><forename type="first">C</forename><surname>Bonini</surname></persName>
		</author>
		<author>
			<persName><forename type="first">M</forename><forename type="middle">R</forename><surname>Boscotrecase</surname></persName>
		</author>
		<author>
			<persName><forename type="first">T</forename><surname>Flagella</surname></persName>
		</author>
		<author>
			<persName><forename type="first">M</forename><surname>Gaspari</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="m">Proceedings ECOOP &apos;89</title>
				<editor>
			<persName><forename type="first">S</forename><surname>Cook</surname></persName>
		</editor>
		<meeting>ECOOP &apos;89<address><addrLine>Nottingham</addrLine></address></meeting>
		<imprint>
			<publisher>Cambridge University Press</publisher>
			<date type="published" when="1989">1989</date>
			<biblScope unit="page" from="243" to="256" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b7">
	<monogr>
		<title level="m" type="main">The Art of the Metaobject Protocol</title>
		<author>
			<persName><forename type="first">G</forename><surname>Kiczales</surname></persName>
		</author>
		<author>
			<persName><forename type="first">J</forename><surname>Des Rivières</surname></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><forename type="middle">G</forename><surname>Bobrow</surname></persName>
		</author>
		<imprint>
			<date type="published" when="1991">1991</date>
			<publisher>MIT Press</publisher>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b8">
	<analytic>
		<title level="a" type="main">CLOS in context -the shape of the design</title>
		<author>
			<persName><forename type="first">D</forename><forename type="middle">G</forename><surname>Bobrow</surname></persName>
		</author>
		<author>
			<persName><forename type="first">R</forename><forename type="middle">P</forename><surname>Gabriel</surname></persName>
		</author>
		<author>
			<persName><forename type="first">J</forename><surname>White</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="m">Object-Oriented Programming: the CLOS perspective</title>
				<editor>
			<persName><forename type="first">A</forename><surname>Paepcke</surname></persName>
		</editor>
		<imprint>
			<publisher>MIT Press</publisher>
			<date type="published" when="1993">1993</date>
			<biblScope unit="page" from="29" to="61" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b9">
	<analytic>
		<title level="a" type="main">Pharo: a reflective language -analyzing the reflective api and its internal dependencies</title>
		<author>
			<persName><forename type="first">I</forename><surname>Thomas</surname></persName>
		</author>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
		<author>
			<persName><forename type="first">P</forename><surname>Tesone</surname></persName>
		</author>
		<author>
			<persName><forename type="first">G</forename><surname>Polito</surname></persName>
		</author>
		<idno type="DOI">10.1016/j.scico.2014.02.016</idno>
	</analytic>
	<monogr>
		<title level="j">Journal of Computer Languages</title>
		<imprint>
			<date type="published" when="2024">2024</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b10">
	<analytic>
		<title level="a" type="main">Handling exceptions</title>
		<author>
			<persName><forename type="first">C</forename><surname>Bera</surname></persName>
		</author>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
		<ptr target="http://books.pharo.org" />
	</analytic>
	<monogr>
		<title level="m">Deep Into Pharo</title>
				<imprint>
			<publisher>Square Bracket Associates</publisher>
			<date type="published" when="2013">2013</date>
			<biblScope unit="page">38</biblScope>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b11">
	<analytic>
		<title level="a" type="main">Handling Error-Handling Errors: dealing with debugger bugs in Pharo</title>
		<author>
			<persName><forename type="first">S</forename><surname>Costiou</surname></persName>
		</author>
		<author>
			<persName><forename type="first">T</forename><surname>Dupriez</surname></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><surname>Pollet</surname></persName>
		</author>
		<ptr target="https://hal.inria.fr/hal-02992644" />
	</analytic>
	<monogr>
		<title level="m">International Workshop on Smalltalk Technologies -IWST 2020</title>
				<imprint>
			<date type="published" when="2020">2020</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b12">
	<analytic>
		<title level="a" type="main">Microdown: a clean and extensible markup language to support pharo documentation</title>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
		<author>
			<persName><forename type="first">L</forename><surname>Dargaud</surname></persName>
		</author>
		<author>
			<persName><forename type="first">G</forename><surname>Polito</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="m">Proceedings of the 2020 International Workshop on Smalltalk Technologies</title>
				<meeting>the 2020 International Workshop on Smalltalk Technologies</meeting>
		<imprint>
			<date type="published" when="2020">2020</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b13">
	<analytic>
		<title level="a" type="main">Statistically rigorous java performance evaluation</title>
		<author>
			<persName><forename type="first">A</forename><surname>Georges</surname></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><surname>Buytaert</surname></persName>
		</author>
		<author>
			<persName><forename type="first">L</forename><surname>Eeckhout</surname></persName>
		</author>
		<idno type="DOI">10.1145/1297027.1297033</idno>
		<idno>doi:10.1145/1297027. 1297033</idno>
		<ptr target="https://doi.org/10.1145/1297027.1297033" />
	</analytic>
	<monogr>
		<title level="m">Proceedings of the 22nd Annual ACM SIGPLAN Conference on Object-Oriented Programming Systems, Languages and Applications, OOPSLA &apos;07</title>
				<meeting>the 22nd Annual ACM SIGPLAN Conference on Object-Oriented Programming Systems, Languages and Applications, OOPSLA &apos;07<address><addrLine>New York, NY, USA</addrLine></address></meeting>
		<imprint>
			<publisher>Association for Computing Machinery</publisher>
			<date type="published" when="2007">2007</date>
			<biblScope unit="page" from="57" to="76" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b14">
	<monogr>
		<title level="m" type="main">Rebench: Execute and document benchmarks reproducibly</title>
		<author>
			<persName><forename type="first">S</forename><surname>Marr</surname></persName>
		</author>
		<idno type="DOI">10.5281/zenodo.1311762</idno>
		<imprint>
			<date type="published" when="2018">2018</date>
		</imprint>
	</monogr>
	<note>version 1.0</note>
</biblStruct>

<biblStruct xml:id="b15">
	<analytic>
		<title level="a" type="main">Spy: A flexible code profiling framework</title>
		<author>
			<persName><forename type="first">A</forename><surname>Bergel</surname></persName>
		</author>
		<author>
			<persName><forename type="first">F</forename><surname>Bañados</surname></persName>
		</author>
		<author>
			<persName><forename type="first">R</forename><surname>Robbes</surname></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><surname>Röthlisberger</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="m">Smalltalks 2010</title>
				<imprint>
			<date type="published" when="2010">2010</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b16">
	<analytic>
		<title level="a" type="main">Spy: A flexible code profiling framework</title>
		<author>
			<persName><forename type="first">A</forename><surname>Bergel</surname></persName>
		</author>
		<author>
			<persName><forename type="first">F</forename><surname>Banados</surname></persName>
		</author>
		<author>
			<persName><forename type="first">R</forename><surname>Robbes</surname></persName>
		</author>
		<author>
			<persName><forename type="first">D</forename><surname>Röthlisberger</surname></persName>
		</author>
		<idno type="DOI">10.1016/j.cl.2011.10.002</idno>
		<ptr target="http://bergel.eu/download/papers/Berg10f-Spy.pdf.doi:10.1016/j.cl.2011.10.002" />
	</analytic>
	<monogr>
		<title level="j">Journal of Computer Languages, Systems and Structures</title>
		<imprint>
			<biblScope unit="volume">38</biblScope>
			<date type="published" when="2011">2011</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b17">
	<analytic>
		<title level="a" type="main">Increasing test coverage with hapao</title>
		<author>
			<persName><forename type="first">A</forename><surname>Bergel</surname></persName>
		</author>
		<author>
			<persName><forename type="first">V</forename><forename type="middle">P</forename><surname>Na</surname></persName>
		</author>
		<idno type="DOI">10.1016/j.scico.2012.04.006</idno>
	</analytic>
	<monogr>
		<title level="j">Science of Computer Programming</title>
		<imprint>
			<biblScope unit="volume">79</biblScope>
			<biblScope unit="page" from="86" to="100" />
			<date type="published" when="2012">2012</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b18">
	<analytic>
		<title level="a" type="main">Sub-method, partial behavioral reflection with reflectivity: Looking back on 10 years of use, The Art</title>
		<author>
			<persName><forename type="first">S</forename><surname>Costiou</surname></persName>
		</author>
		<author>
			<persName><forename type="first">V</forename><surname>Aranega</surname></persName>
		</author>
		<author>
			<persName><forename type="first">M</forename><surname>Denker</surname></persName>
		</author>
		<idno type="DOI">10.22152/programming-journal.org/2020/4/5</idno>
	</analytic>
	<monogr>
		<title level="j">Science, and Engineering of Programming</title>
		<imprint>
			<biblScope unit="volume">4</biblScope>
			<date type="published" when="2020">2020</date>
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b19">
	<analytic>
		<title level="a" type="main">Higher abstractions for dynamic analysis</title>
		<author>
			<persName><forename type="first">M</forename><surname>Denker</surname></persName>
		</author>
		<author>
			<persName><forename type="first">O</forename><surname>Greevy</surname></persName>
		</author>
		<author>
			<persName><forename type="first">M</forename><surname>Lanza</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="m">2nd International Workshop on Program Comprehension through Dynamic Analysis</title>
				<meeting><address><addrLine>PCODA</addrLine></address></meeting>
		<imprint>
			<date type="published" when="2006">2006. 2006</date>
			<biblScope unit="page" from="32" to="38" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b20">
	<analytic>
		<title level="a" type="main">Sub-method reflection</title>
		<author>
			<persName><forename type="first">M</forename><surname>Denker</surname></persName>
		</author>
		<author>
			<persName><forename type="first">S</forename><surname>Ducasse</surname></persName>
		</author>
		<author>
			<persName><forename type="first">A</forename><surname>Lienhard</surname></persName>
		</author>
		<author>
			<persName><forename type="first">P</forename><surname>Marschall</surname></persName>
		</author>
		<idno type="DOI">10.5381/jot.2007.6.9.a14</idno>
	</analytic>
	<monogr>
		<title level="m">Proceedings of TOOLS Europe</title>
				<meeting>TOOLS Europe</meeting>
		<imprint>
			<publisher>ETH</publisher>
			<date type="published" when="2007">2007. 2007</date>
			<biblScope unit="volume">6</biblScope>
			<biblScope unit="page" from="231" to="251" />
		</imprint>
	</monogr>
</biblStruct>

<biblStruct xml:id="b21">
	<monogr>
		<title level="m" type="main">Sub-method Structural and Behavioral Reflection</title>
		<author>
			<persName><forename type="first">M</forename><surname>Denker</surname></persName>
		</author>
		<imprint>
			<date type="published" when="2008">2008</date>
		</imprint>
		<respStmt>
			<orgName>University of Bern</orgName>
		</respStmt>
	</monogr>
	<note type="report_type">PhD thesis</note>
</biblStruct>

<biblStruct xml:id="b22">
	<analytic>
		<title level="a" type="main">Efficient method dispatch in PCL</title>
		<author>
			<persName><forename type="first">G</forename><surname>Kiczales</surname></persName>
		</author>
		<author>
			<persName><forename type="first">L</forename><surname>Rodriguez</surname></persName>
		</author>
	</analytic>
	<monogr>
		<title level="m">Proceedings of ACM conference on Lisp and Functional Programming</title>
				<meeting>ACM conference on Lisp and Functional Programming<address><addrLine>Nice</addrLine></address></meeting>
		<imprint>
			<date type="published" when="1990">1990</date>
			<biblScope unit="page" from="99" to="105" />
		</imprint>
	</monogr>
</biblStruct>

				</listBibl>
			</div>
		</back>
	</text>
</TEI>
