<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Andrey Cheboksarov</title>
    <description>The latest articles on Forem by Andrey Cheboksarov (@0xaa4eb).</description>
    <link>https://forem.com/0xaa4eb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F692818%2Fe606e1fc-0f81-48b7-be6c-8feb9552fa8f.png</url>
      <title>Forem: Andrey Cheboksarov</title>
      <link>https://forem.com/0xaa4eb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/0xaa4eb"/>
    <language>en</language>
    <item>
      <title>Reverse engineer Spring Web in 5 minutes using a recording debugger</title>
      <dc:creator>Andrey Cheboksarov</dc:creator>
      <pubDate>Wed, 18 Dec 2024 07:16:48 +0000</pubDate>
      <link>https://forem.com/0xaa4eb/reverse-engineer-spring-web-in-5-minutes-using-a-recording-debugger-lol</link>
      <guid>https://forem.com/0xaa4eb/reverse-engineer-spring-web-in-5-minutes-using-a-recording-debugger-lol</guid>
      <description>&lt;p&gt;Large Java frameworks can be reverse engineered with help of some tools. The article shows how to dig into Spring Web internals with help of &lt;a href="https://github.com/0xaa4eb/ulyp" rel="noopener noreferrer"&gt;ulyp&lt;/a&gt; in less than 10 minutes. A developer can observe how framework works inside before diving into source code. This is a part 2 of the ongoing blog series about ulyp. The first part is available &lt;a href="https://dev.to/0xaa4eb/ulyp-recording-java-code-execution-for-faster-debugging-part-1-3a57"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;When working with gigantic frameworks, the main issue is to analyze their codebase. Sometimes, a developer needs to get understanding of internals real quick. This can happen when something doesn't work as expected, yet basic debugging doesn't help much.&lt;/p&gt;

&lt;p&gt;Some of the ways to get into the codebase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use plain old debugger. Stop the app at some breakpoint and check the stack trace and surrounding types.&lt;/li&gt;
&lt;li&gt;Dive right to the source code with the obtained knowledge.&lt;/li&gt;
&lt;li&gt;Start from tests. Tests can be quite helpful if one wants to understand the codebase.&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;TRACE&lt;/code&gt; logs and try to gather some info from them.&lt;/li&gt;
&lt;li&gt;Use a sampling profiler. Building a flamegraph could be quite a good idea to start from. This can be quite useful if the app being analyzed is already under load.&lt;/li&gt;
&lt;li&gt;Use a recording debugger, which records the whole execution flow and then can show the call tree.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The article focuses on the last approach. In certain cases, using such a tool might be useful for a anybody who works with large codebase. Recorded execution flow can be used to get the idea how things work at runtime. A developer can then dive  into source code with good understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reverse engineering Spring Web
&lt;/h3&gt;

&lt;p&gt;We will see how the tool works on Spring Web based app. The project which we are going to &lt;br&gt;
use is &lt;a href="https://github.com/spring-petclinic/spring-petclinic-rest" rel="noopener noreferrer"&gt;spring-petclinic&lt;/a&gt;. This is a basic web app written on Java with Spring Web. &lt;br&gt;
Suppose, we want to understand how it works. And we want to do it really quick.&lt;/p&gt;

&lt;p&gt;We will skip the setup and jump right to recording. The first thing we want to know is where to start recording. This can be done by plenty of ways, but the easiest way is to gather some stack traces. If we stop inside the controller we see the top of the stacktrace is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;app methods&amp;gt;
    ...
    &amp;lt;other spring methods&amp;gt;
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    ...
    &amp;lt;tomcat methods&amp;gt;
    at java.base/java.lang.Thread.run(Thread.java:1583)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we want to reverse engineer Spring Web, we'd like to start from the bottom of the stacktrace. This is &lt;code&gt;org.springframework.web.filter.OncePerRequestFilter&lt;/code&gt;&lt;br&gt;
in our case. It's a first called method of Spring framework.&lt;/p&gt;

&lt;p&gt;If we want to record execution, we should configure ulyp first. We specify the following system properties when start petclinic app, &lt;br&gt;
no code change is required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-javaagent:/home/tools/ulyp/ulyp-agent-1.0.2.jar
-Dulyp.methods=**.OncePerRequestFilter.doFilter
-Dulyp.file=/tmp/test-petclinic.dat
-Dulyp.record-constructors
-Dulyp.record-collections=JDK
-Dulyp.record-arrays
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifying &lt;code&gt;-Dulyp.methods=**.OncePerRequestFilter.doFilter&lt;/code&gt; makes ulyp start recording whenever &lt;code&gt;doFilter&lt;/code&gt; method of any class named &lt;code&gt;OncePerRequestFilter&lt;/code&gt; is called. &lt;code&gt;-Dulyp.file=/tmp/test-petclinic.dat&lt;/code&gt; specifies the output filepath. The other props enable additional recording features which is optional.&lt;/p&gt;

&lt;p&gt;We open the UI at url &lt;code&gt;http://localhost:4200/petclinic/vets&lt;/code&gt; which lists all veterinarians. We stop the app after, since ulyp &lt;br&gt;
writes everything to the output file immediately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ejji0vy03tfkhxa74l0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ejji0vy03tfkhxa74l0.png" alt="petclinic app" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we want to see what's recorded. ulyp comes with the desktop app which can open recording files. If we open &lt;code&gt;/tmp/test-petclinic.dat&lt;/code&gt; file, we can observe there is indeed some recorded data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ujfp5rbdl89ebjbsbn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ujfp5rbdl89ebjbsbn5.png" alt="ulyp recording data" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we see is a &lt;strong&gt;recording call tree&lt;/strong&gt;. The top method is &lt;code&gt;OncePerRequestFilter.doFilter&lt;/code&gt; as we expected. The next level are&lt;br&gt;
methods which were called inside the method call (basically, children). Black background line hints how many calls are inside every subtree.&lt;br&gt;
We can unfold every method call which has nested calls recorded. If we unfold further down, we can get the overall idea. &lt;br&gt;
Spring Web implements &lt;a href="https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern" rel="noopener noreferrer"&gt;chain of responsibility&lt;/a&gt; pattern. So, &lt;br&gt;
every filter does some work and then calls the next filter. Here we see that &lt;code&gt;OrderedFormContentFilter&lt;/code&gt; calls &lt;br&gt;
&lt;code&gt;OrderedRequestContextFilter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft106sw53thfaa7xmu7ih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft106sw53thfaa7xmu7ih.png" alt="Spring MVC filters" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can read more about Servlet based application architecture &lt;a href="https://docs.spring.io/spring-security/reference/servlet/architecture.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If we dig down, we can observe how Spring Web creates &lt;a href="https://github.com/micrometer-metrics/micrometer" rel="noopener noreferrer"&gt;micrometer&lt;/a&gt; events. &lt;br&gt;
Other stuff is updating context which can be accessed from other filters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm239waan2r3kkyotgo5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm239waan2r3kkyotgo5w.png" alt="micrometer event creation" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are also many filters which are called on the path. The next interesting one is &lt;code&gt;AuthorizationFilter&lt;/code&gt;. We can &lt;br&gt;
see how it calls &lt;code&gt;ObservationAuthorizationManager.check&lt;/code&gt; method, which returns an instance of &lt;code&gt;AuthorizationDecision&lt;/code&gt;. &lt;br&gt;
The filter later checks that &lt;code&gt;AuthorizationDecision.isGranted&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;, which means the user has the access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4njpt8s8js02wq9q248.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4njpt8s8js02wq9q248.png" alt="AuthorizationFilter" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we can see how &lt;code&gt;DispatcherServlet.doDispatch&lt;/code&gt; is called, which does some logic, and finally we see our controller is called. &lt;br&gt;
The &lt;code&gt;VetController&lt;/code&gt; returned a list of &lt;code&gt;Vet&lt;/code&gt; objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fite0c5i0xta94vckkjgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fite0c5i0xta94vckkjgw.png" alt="VetController called" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, we can check how Spring MVC works, calls filters, validators, maps response into json, etc. &lt;br&gt;
Every method is recorded which later can be analyzed.&lt;/p&gt;

&lt;p&gt;If we wanted it, we could dig down to Hibernate or even JDBC level, but that's not the point of the article. The first &lt;a href="https://dev.to/0xaa4eb/ulyp-recording-java-code-execution-for-faster-debugging-part-1-3a57"&gt;part&lt;/a&gt; of the blog series shows how ulyp records Hibernate internals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The article shown how a developer can record and analyze the internals of large framework. The obtained knowledge can &lt;br&gt;
help in development. Feel free to try &lt;a href="https://github.com/0xaa4eb/ulyp" rel="noopener noreferrer"&gt;ulyp&lt;/a&gt; while working on a huge codebase.&lt;/p&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>springboot</category>
      <category>jvm</category>
    </item>
    <item>
      <title>Ulyp: Recording Java code execution for faster debugging (Part 1)</title>
      <dc:creator>Andrey Cheboksarov</dc:creator>
      <pubDate>Sun, 13 Oct 2024 06:41:30 +0000</pubDate>
      <link>https://forem.com/0xaa4eb/ulyp-recording-java-code-execution-for-faster-debugging-part-1-3a57</link>
      <guid>https://forem.com/0xaa4eb/ulyp-recording-java-code-execution-for-faster-debugging-part-1-3a57</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the first part of a 10-post series describing the background and motivation behind Ulyp.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The article presents &lt;a href="https://github.com/0xaa4eb/ulyp" rel="noopener noreferrer"&gt;Ulyp&lt;/a&gt; which is an open-source instrumentation agent that records method calls (including argument and return values) of all third-party libraries of JVM apps. Software engineer can later upload a recording file to the UI desktop app in order to better understand the internals of libraries and even the whole applications. The tool can help developers understand the internals of frameworks faster, gain deeper insights, find inefficiencies in software, as well as debug more effectively.&lt;br&gt;
In a few words, Ulyp converts this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LoadingCache&amp;lt;Integer, DatabaseEntity&amp;gt; cache = Caffeine.newBuilder()
    ...
    .build(databaseSource::findById);

DatabaseEntity fromCache = cache.get(5);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fff5bao30cr4pk5rsbacj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fff5bao30cr4pk5rsbacj.png" alt="Image description" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges in Modern Software engineering
&lt;/h2&gt;

&lt;p&gt;The software engineering field nowadays is completely different from what it was 10 years ago. As scale of IT solutions rises, the complexity of systems is now is on entirely different level. A typical application may have thousands of instances in multiple availability zones. At the same time, the amount of code is usually around hundreds thousands lines.&lt;/p&gt;

&lt;p&gt;The number of frameworks and libraries used as a dependency in a typical app is also higher than it was before. When it comes to gigantic frameworks which require tons of boilerplate, Java language is somewhat notorious. The ecosystem carries the largest frameworks ever created. Some examples are Spring, Hibernate and others.&lt;/p&gt;

&lt;p&gt;One of the problem a modern Java developer faces is understanding code quickly. An average engineer spends way more time reading code than writing it. Understanding how libraries and frameworks work under the hood and what they do is extremely vital for proper software development.&lt;/p&gt;

&lt;p&gt;Another problem is debugging a running instance of an application on some environment where classic debugger might not be available. Usually, it’s possible to use logs and APM tracers, but these tools might not always suffice the needs.&lt;/p&gt;

&lt;p&gt;One possible way to mitigate some of these problems is code execution recording for later analysis. The idea is by far not new as there are already dozens of time-travel debuggers for different languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Ulyp: A recording debugger for Java/Kotlin
&lt;/h2&gt;

&lt;p&gt;Ulyp is an instrumentation agent written specifically for this task. Recording all function calls along with return values and arguments is possible thanks to JVM bytecode instrumentation. This effectively eliminates the need of breakpoints in certain cases. It’s also feasible to record the execution flow in several apps at the same time via remote control. A developer is able to record and observe what happened in a distributed environment.&lt;/p&gt;

&lt;p&gt;All data is dumped to the file a user specified via system properties. There are auxiliary threads which do the job of converting objects to binary format and writing to file. The resulting file can later be opened in UI app and entire flow can be analyzed.&lt;/p&gt;

&lt;p&gt;Currently, Ulyp uses &lt;a href="https://github.com/raphw/byte-buddy" rel="noopener noreferrer"&gt;bytebuddy&lt;/a&gt; library which does an immense job of handling all the work of instrumentation and makes it extremely easy for all Java developers. The rest is relatively straightforward to implement. The ongoing blogposts will shed a light on how the tool is implemented. Right now, let’s move to action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case Study: Hibernate internals
&lt;/h2&gt;

&lt;p&gt;Let’s start with on of the well known Java frameworks — Hibernate. We are going to use Spring to setup the demo and use Hiberante with H2 embedded data source. We will write very simple code snippet which takes a newly created entity and persists it to the database. We then observe the execution flow by recording it using Ulyp.&lt;/p&gt;

&lt;p&gt;The demo is quite straightforward. Our entity is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
    public class Person {

        @Id
        @GeneratedValue
        private long id;
        @Basic
        private String firstName;
        @Basic
        private String lastName;
        @Basic
        private String phoneNumber;
        @Basic
        private int age;
        ...
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also have a JPA repository to save entities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Repository
    public interface PersonRepository extends JpaRepository&amp;lt;Person, Long&amp;gt; {

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also a service which is marked with transactional annotation, so that it opens a transaction whenever any of its’ public method is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
@Transactional
public class PersonStoreService {

    @Autowired
    private PersonRepository repository;

    public void save(Person person) {
        repository.save(person);
    }

    public Person find(long id) {
        return repository.findOne(id);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have main method which looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Configuration.class);
        PersonStoreService store = context.getBean(PersonStoreService.class);

        Person person = new Person();
        person.setFirstName("John");
        person.setLastName("Doe");
        person.setAge(42);

        store.save(person);
        System.out.println("Stored: " + person);

        Person foundPerson = store.find(person.getId());
        System.out.println("Found: " + foundPerson);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole code is available at the repo. After we execute the code, it will print the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stored: org.example.hibernate.util.Person@52d14571
Found: org.example.hibernate.util.Person@75268ef2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’d like to understand what exactly happened when we call the code. Let’s start using Ulyp. The only thing we need to do is to specify system properties, nothing else is changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-javaagent:/home/user/ulyp-agent-0.3.1-SNAPSHOT.jar
-Dulyp.file=/tmp/test-spring.dat
-Dulyp.methods=**.PersonStoreService.*
-Dulyp.record-constructors
-Dulyp.record-collections=JDK
-Dulyp.record-arrays
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first property tells the JVM that the agent should be used. The second property &lt;code&gt;ulyp.file&lt;/code&gt; specifies file path where ulyp should store all recorded data. The next property tells Ulyp when it should start recording. In this case, whenever any method of class named &lt;code&gt;PersonStoreService&lt;/code&gt; is called, ulyp will start recording all nested calls. &lt;code&gt;ulyp.record-constructors&lt;/code&gt; tells to instrument and record constructors, and &lt;code&gt;ulyp.record-collections=JDK&lt;/code&gt; allows to partially record collection values, but only for JDK standard library collections. The collections itself are not instrumented, i.e. Ulyp do not record calls like &lt;code&gt;ArrayList.add&lt;/code&gt;, but can record some items from &lt;code&gt;ArrayList&lt;/code&gt; if it is passed as an argument (or returned from) to some recorded method call.&lt;/p&gt;

&lt;p&gt;After running the demo with properties set, we can upload the file to UI desktop app. The first thing we see is the list of recorded methods on the left.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6w3op70hzetvoqcr86a3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6w3op70hzetvoqcr86a3.png" alt="List of recorded methods" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every entry in the list is a recorded method of some instance of &lt;code&gt;PersonStoreService&lt;/code&gt; class. We can also see the duration of methods, as well as the number of recorded calls inside. There are 90,000 calls for just storing an entity with Spring, Hibernate and H2 inside. But keep in mind, there is a lot of bootrstrapping is done once the method is called for the first time. Next call would probably have less methods recorded. Duration is also quite high for storing just a single entity. There is an explanation to this — not only there was not enough time for JVM to warm up, but we also instrumented all third party classes, as well as recorded all method calls. There are benchmarks which measure the overhead of instrumenting and recording apps. The data shows that for a typical enterprise app, the overhead is around x3-x5 with proper warm up comparing to not using the agent at all.&lt;/p&gt;

&lt;p&gt;Another thing you probably noticed, is that the name of the class is &lt;code&gt;PersonStoreService$$EnhancerBySpringCGLIB$$fd7728fe&lt;/code&gt; rather that just &lt;code&gt;PersonStoreService&lt;/code&gt;. That’s simply because the class is marked with &lt;code&gt;@Transactional&lt;/code&gt; annotation. Spring will create a proxy for managing transactions, and then recording starts upon the proxy call.&lt;/p&gt;

&lt;p&gt;Let’s dive deep into save method recorded calls. The recording data is presented in form of a tree with collapsible nodes. Every node is a recorded method call. Nodes are also marked with black pane which hints how many nested calls a node has comparing to the other nodes. The first thing we can see is that Spring proxy actually calls the instance of &lt;code&gt;TransactionInterceptor&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4mh2nqk6x5s6oazhjx88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4mh2nqk6x5s6oazhjx88.png" alt="TransactionInterceptor inside proxy" width="800" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we dive deeper, we can see that our &lt;code&gt;PersonStoreService&lt;/code&gt; is called after the transaction is started. Our transaction manager is an instance of &lt;code&gt;JpaTransactionManager&lt;/code&gt; class. We can also see a call to &lt;code&gt;TransactionInterceptor.commitTransactionAfterReturning&lt;/code&gt; where the opened transaction is supposedly commited.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0skaxx9wv3lmzfgx5nye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0skaxx9wv3lmzfgx5nye.png" alt="Image description" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The further exploration points where the instance of &lt;code&gt;EntityManager&lt;/code&gt; is called:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcc0pdwjof59ar2i3usd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcc0pdwjof59ar2i3usd.png" alt="Image description" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point we see only simple class names (i.e. no package name), and it might be hard to tell which classes belong to Hibernate. That’s exactly why Ulyp can show full class names. Just select a node and hold the Shift button, and it you will see fully qualified names:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6c1bxtrlh28xe3l5ghmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6c1bxtrlh28xe3l5ghmj.png" alt="Image description" width="800" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we dive even deeper, we start seeing Hibernate &lt;code&gt;SessionImpl&lt;/code&gt; in action. It calls &lt;code&gt;JpaPersistEventListener.onPersist&lt;/code&gt; method:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqc1vgjr08qfg58zdm12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqc1vgjr08qfg58zdm12.png" alt="Image description" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next method gets primary key value from the sequence for inserting the entuty into the table. First, JDBC statement is prepared. Then, the result set is obtained. Finally, we get primary key value (which is “1”) for storing our entity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrgdt4fzqxggsnyiiw1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrgdt4fzqxggsnyiiw1e.png" alt="Image description" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we start seeing JDBC layer, prepared statements to be exact. Those prepared statements are a part of H2 library:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7wmbhxnwr4e0cuzkny6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7wmbhxnwr4e0cuzkny6.png" alt="Image description" width="800" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, this newly obtained primary key is passed to &lt;code&gt;JpaPersistEventListener&lt;/code&gt;. However, we do not see any inserts here! The only thing we see is attaching an entity to the instance of &lt;code&gt;StatefulPersistenctConext&lt;/code&gt;. The actual inserting to the database will happen when Hibernate session is flushed later. Session flush may happen at either commit time, or when the entity table is queried inside the ongoing transaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xez81duf28xhbf0lscx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xez81duf28xhbf0lscx.png" alt="Image description" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to confirm this guess, we can check the recorded internals of commit method we saw earlier. Just dive into &lt;code&gt;TransactionInterceptor.commitTransactionAfterReturning&lt;/code&gt;. After expanding some nodes, you can see the insert into the table with actual values:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfi4lt1znuvzf0st6jpa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfi4lt1znuvzf0st6jpa.png" alt="Image description" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was the most simple demo for using Ulyp. Other cases include (but not limited to):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tracing distributed systems running in clouds&lt;/li&gt;
&lt;li&gt;gathering gigs of data for later analysis&lt;/li&gt;
&lt;li&gt;recording realtime apps where stopping a thread using a conventional debugger is not an option&lt;/li&gt;
&lt;li&gt;finding inefficiencies in a gigantic frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ulyp doesn’t try to solve all the existing problems and it’s definitely not a silver bullet. The overhead of instrumenting could be quite high. You might now want to run it on production environment, but dev/test are usually ok. But if one can run their software app locally or on dev environment, it opens the opportunity to see things at completely different angle.&lt;/p&gt;

&lt;p&gt;Ulyp is fully open-source and is available at &lt;a href="https://github.com/0xaa4eb/ulyp" rel="noopener noreferrer"&gt;https://github.com/0xaa4eb/ulyp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Part 1 has just covered a relatively basic case, Part2 will cover some more examples for Spring Boot. Future blog posts will cover more sophisticated use cases like recording several apps in a distributed system. Subscribe and stay tuned.&lt;/p&gt;

</description>
      <category>jvm</category>
      <category>java</category>
      <category>kotlin</category>
    </item>
  </channel>
</rss>
