In Java application development, function-level monitoring has traditionally been a high-risk endeavor. It often necessitates modifying source code, adding logs, and setting breakpoints—operations that are not only time-consuming and resource-intensive but can also introduce potential risks in production environments. Many teams thus find themselves in a dilemma: either sacrifice efficiency by incurring the overhead of code modification, or compromise visibility by foregoing real-time insight into critical functions.

OpenResty XRay’s Java Function Profiler was specifically designed to address this challenge. It enables function monitoring through a completely non-invasive approach, allowing developers to maintain production environment stability while precisely capturing runtime details when needed.

What is Non-Intrusive Function Probing?

When troubleshooting system issues or optimizing performance, we often wish to gain insight into the internal workings of a specific function. However, the challenge is that most of the time, you cannot directly modify third-party code. This is where non-intrusive function probing becomes invaluable. Its primary benefit is the ability to monitor and measure the behavior of target methods without altering their source code.

This approach is also known as “non-intrusive” monitoring: it allows for deep observation without the need to recompile, redeploy, or modify the source code. For engineers, this significantly reduces both risks and costs.

Intrusive vs. Non-Intrusive: What’s the Difference?

Many might ask: haven’t probes always existed? What makes “non-intrusive” so novel?

FeatureIntrusive ProbingNon-Intrusive Function Probing
Implementation MethodModifying code, adding stubs, recompilingModifying memory or runtime mechanisms (Hooking / Instrumentation)
IntrusivenessHigh (requires code modification)Low/None (no code modification, only intercepts execution flow)
Applicable ScenariosDevelopment/testing, where source code is controllableProduction environments, third-party libraries, unmodifiable source code
RisksProne to introducing bugs, more complex version managementMain risk is hooking failure or improper operation leading to crashes

Intrusive methods are more suitable for development environments, while non-intrusive methods are the preferred choice for production environments.

Why Are Engineers Increasingly Prioritizing “Non-Intrusive Probes”?

When system issues arise, the most daunting challenge isn’t the bug itself, but the inability to pinpoint its location. Non-intrusive probes act like “X-ray vision,” enabling you to observe real-time function execution details without disrupting service.

The direct benefits they offer include:

  • Troubleshooting: For production issues, you can identify call details without any downtime.
  • Performance Analysis: Precisely locate underperforming functions that hinder performance and pinpoint bottlenecks.
  • Security Auditing: Monitor sensitive function calls and detect suspicious behavior immediately.

In essence, user experience remains completely unaffected, yet you’re able to uncover all problem clues in the background. This is the core strength of non-intrusive probes.

What Makes OpenResty XRay’s Non-Intrusive Probes Unique?

Compared to traditional probes, OpenResty XRay’s approach is better aligned with the actual demands of production environments:

  • Zero Code Modification: No need to alter any code in the target program.
  • Comprehensive Coverage: Capable of tracing both interpreted execution and JIT-optimized functions.
  • Parameter Acquisition: Real-time retrieval of parameter values during function calls.
  • Dynamic Monitoring: Can be enabled and disabled at runtime, allowing flexible control over probe granularity.

A minor caveat, however: inline functions currently cannot be monitored because they lack independent function entry points.

Hands-on Demo: Monitoring Function Parameters

Let’s demonstrate how to use function probes with a concrete example. Suppose we have the following Java method defined:

public class UserService {
    public static class User {
        private String email;

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }
    }

    public String greetUser(String name, int age, User user) {
        return "Hello " + name;
    }
}

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        User user = new User();
        user.setEmail("tom@example.com");
        userService.greetUser("Tom", 25, user);
    }
}, 0, 1000);

This method accepts three parameters: a string name, an integer age, and an object user. Our goal is to monitor the values of these parameters without modifying the code.

Step One: Obtain Function Entry Address

First, we need to use ylang to retrieve the entry address of the target method:

_probe _process.begin {
    find_method_entry("UserService", "greetUser", "(Ljava/lang/String;ILUserService$User;)Ljava/lang/String;");
    _exit();
}

Provide the class name, method name, and method signature, respectively.

This command will return the entry address of the greetUser method, which is crucial information for setting up the probe.

type: compiled, class: UserService, method: greetUser, signature: (Ljava/lang/String;ILUserService$User;)Ljava/lang/String;, entry: 0x7fffe0d2e20c, code_begin: 0x7fffe0d2e1e0, code_end: 0x7fffe0d2e7a0
type: interpreted, class: UserService, method: greetUser, signature: (Ljava/lang/String;ILUserService$User;)Ljava/lang/String;, entry: 0x7fffe046b540, code_begin: 0x7fff6b400730, code_end: 0x7fff6b400749

Step Two: Setting up the Function Probe

Next, we use the acquired JIT method entry address as the watchpoint address to configure a method call interceptor:

#include "jvm.y"

_probe _watchpoint(0x7fffe0d2e20c).exec
{
    _str name = get_java_string(nmethod_oop_arg(2));
    int age = nmethod_int_arg(3);
    oop email_obj = dump_field_object(nmethod_oop_arg(4), "email");
    _str email = get_java_string(email_obj);
    printf("name: %s, age: %d, email: %s\n", name, age, email);
    _exit();
}

The primary purpose of this probe code is to automatically capture parameter information when a Java method is invoked, thereby assisting developers in analyzing function behavior without interrupting services. Specifically, its execution flow can be broken down into the following key steps:

  • _watchpoint(ENTRY).exec: Sets a watchpoint at the target method’s entry. Once the method is called, the probe logic will be triggered.

  • nmethod_oop_arg(2), nmethod_int_arg(3), nmethod_oop_arg(4): These retrieve the 2nd, 3rd, and 4th parameters of the invoked method.

  • It’s important to note that in Java instance methods, the 1st parameter defaults to the this object. Therefore, the actual business-relevant parameters begin at index 2.

  • get_java_string(): Used to read a string object from the Java runtime as a printable string value.

  • nmethod_int_arg(): Used to extract integer-type parameter values.

  • dump_field_object(user_obj, "email"): Extracts the object corresponding to the email field from the User object.

  • get_java_string(email_obj): Converts the extracted email field object into a string for logging or subsequent analysis.

Through this probe logic, we can capture Java method parameter values in real-time without modifying the source code, and further extract key fields within objects. This provides significant observability for troubleshooting and behavior analysis.

Execution Results

Upon execution of the target method, the ylang executor will output the following monitoring data:

name: Tom, age: 25, email: tom@example.com

Technical Advantages and Use Cases

Once a Java application enters the production environment, many problems become elusive: logs are limited, performance analysis tools cannot be easily deployed, and debuggers are simply not an option. Yet, the business continues to run, and users are actively accessing it – you can neither shut down the system nor modify its source code. So, how can you gain deep insight into the system’s actual behavior?

Function Probes were developed specifically to address this pain point. OpenResty XRay can dynamically capture critical function-level runtime information without disturbing the normal operation of the system.

  1. Performance Diagnosis: A Microscope for Deep Insight

Performance bottlenecks in Java applications are often deeply hidden: a slow database call, a high-frequency but inefficient utility method. Without fine-grained runtime data, locating such problems is almost like looking for a needle in a haystack. Function probes can directly capture the invocation time, input parameters, and return values of target methods at runtime. This acts like a “microscope,” enabling developers to pinpoint issues precisely at the function level.

  1. Production Environment: The Safety Net

“Test environment normal, production environment crashed” is every technical team’s nightmare. However, you can never arbitrarily restart or add debugging code during peak hours. This is where the advantage of non-intrusive function probes comes in: no need to modify source code or restart the application. They allow you to dynamically attach and detach probes, enabling an “on-demand” approach. This means you can gather insights without introducing additional risks to the live system.

  1. Security and Compliance: The Sentinel

In sensitive scenarios, such as functions involving encryption, authentication, or financial transactions, teams need to monitor invocation patterns in real-time to ensure no abnormal behavior. Probes provide deep monitoring capabilities for critical function call traces without exposing core source code logic. This offers first-hand, irrefutable data support for compliance auditing and security protection.

  1. Complex Environment Debugging: The Last Mile

For Java engineers, debugging typically relies on IDEs, breakpoints, and logs. However, in containerized, distributed, and cloud-native environments, these methods are often ineffective. Function probes offer a more lightweight approach that still allows you to capture the most critical function-level information in complex environments. In actual Java production scenarios, function probes are not merely a nice-to-have; they are a crucial means to overcome the challenge of “production environment observability.” They help developers and operations teams avoid detours in performance optimization, problem troubleshooting, security monitoring, and other areas, achieving greater certainty with reduced costs.

What is OpenResty XRay

OpenResty XRay is a dynamic-tracing product that automatically analyzes your running applications to troubleshoot performance problems, behavioral issues, and security vulnerabilities with actionable suggestions. Under the hood, OpenResty XRay is powered by our Y language targeting various runtimes like Stap+, eBPF+, GDB, and ODB, depending on the contexts.

If you like this tutorial, please subscribe to this blog site and/or our YouTube channel. Thank you!

About The Author

Yichun Zhang (Github handle: agentzh), is the original creator of the OpenResty® open-source project and the CEO of OpenResty Inc..

Yichun is one of the earliest advocates and leaders of “open-source technology”. He worked at many internationally renowned tech companies, such as Cloudflare, Yahoo!. He is a pioneer of “edge computing”, “dynamic tracing” and “machine coding”, with over 22 years of programming and 16 years of open source experience. Yichun is well-known in the open-source space as the project leader of OpenResty®, adopted by more than 40 million global website domains.

OpenResty Inc., the enterprise software start-up founded by Yichun in 2017, has customers from some of the biggest companies in the world. Its flagship product, OpenResty XRay, is a non-invasive profiling and troubleshooting tool that significantly enhances and utilizes dynamic tracing technology. And its OpenResty Edge product is a powerful distributed traffic management and private CDN software product.

As an avid open-source contributor, Yichun has contributed more than a million lines of code to numerous open-source projects, including Linux kernel, Nginx, LuaJIT, GDB, SystemTap, LLVM, Perl, etc. He has also authored more than 60 open-source software libraries.