<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Observability Labs - Articles by Israel Ogbole</title>
        <link>https://www.elastic.co/observability-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Wed, 22 Apr 2026 15:41:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Observability Labs - Articles by Israel Ogbole</title>
            <url>https://www.elastic.co/observability-labs/assets/observability-labs-thumbnail.png</url>
            <link>https://www.elastic.co/observability-labs</link>
        </image>
        <copyright>© 2026. Elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[Beyond the trace: Pinpointing performance culprits with continuous profiling and distributed tracing correlation]]></title>
            <link>https://www.elastic.co/observability-labs/blog/continuous-profiling-distributed-tracing-correlation</link>
            <guid isPermaLink="false">continuous-profiling-distributed-tracing-correlation</guid>
            <pubDate>Thu, 28 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Frustrated by slow traces but unsure where the code bottleneck lies? Elastic Universal Profiling correlates profiling stacktraces with OpenTelemetry (OTel) traces, helping you identify and pinpoint the exact lines of code causing performance issues.]]></description>
            <content:encoded><![CDATA[<p>Observability goes beyond monitoring; it's about truly understanding your system. To achieve this comprehensive view, practitioners need a unified observability solution that natively combines insights from metrics, logs, traces, and crucially, <strong>continuous profiling</strong>. While metrics, logs, and traces offer valuable insights, they can't answer the all-important &quot;why.&quot; Continuous profiling signals act as a magnifying glass, providing granular code visibility into the system's hidden complexities. They fill the gap left by other data sources, enabling you to answer critical questions –– why is this trace slow? Where exactly in the code is the bottleneck residing?</p>
<p>Traces provide the &quot;what&quot; and &quot;where&quot; — what happened and where in your system. Continuous profiling refines this understanding by pinpointing the &quot;why&quot; and validating your hypotheses about the &quot;what.&quot; Just like a full-body MRI scan, Elastic's whole-system continuous profiling (powered by eBPF) uncovers unknown-unknowns in your system. This includes not just your code, but also third-party libraries and kernel activity triggered by your application transactions. This comprehensive visibility improves your mean-time-to-detection (MTTD) and mean-time-to-recovery (MTTR) KPIs.</p>
<p><em>[Related article:</em> <a href="https://www.elastic.co/blog/observability-profiling-metrics-logs-traces"><em>Why metrics, logs, and traces aren’t enough</em></a><em>]</em></p>
<h2>Bridging the disconnect between continuous profiling and OTel traces</h2>
<p>Historically, continuous profiling signals have been largely disconnected from OpenTelemetry (OTel) traces. Here's the exciting news: we're bridging this gap! We're introducing native correlation between continuous profiling signals and OTel traces, starting with Java.</p>
<p>Imagine this: You're troubleshooting a performance issue and identify a slow trace. Whole-system continuous profiling steps in, acting like an MRI scan for your entire codebase and system. It narrows down the culprit to the specific lines of code hogging CPU time within the context of your distributed trace. This empowers you to answer the &quot;why&quot; question with minimal effort and confidence, all within the same troubleshooting context.</p>
<p>Furthermore, by correlating continuous profiling with distributed tracing, Elastic Observability customers can measure the cloud cost and CO&lt;sub&gt;2&lt;/sub&gt; impact of every code change at the service and transaction level.</p>
<p>This milestone is significant, especially considering the recent developments in the OTel community. With <a href="https://www.cncf.io/blog/2024/03/19/opentelemetry-announces-support-for-profiling/">OTel adopting profiling</a> and Elastic <a href="https://www.elastic.co/blog/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry">donating the industry’s most advanced eBPF-based continuous profiling agent to OTel</a>, we're set for a game-changer in observability — empowering OTel end users with a correlated system visibility that goes from a trace span in the userspace down to the kernel.</p>
<p>Furthermore, achieving this goal, especially with Java, presented significant challenges and demanded serious engineering R&amp;D. This blog post will delve into these challenges, explore the approaches we considered in our proof-of-concepts, and explain how we arrived at a solution that can be easily extended to other OTel language agents. Most importantly, this solution correlates traces with profiling signals at the agent, not in the backend — to ensure optimal query performance and minimal reliance on vendor backend storage architectures.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/continuous-profiling-distributed-tracing-correlation/trace.png" alt="Profiling flamegraph for a specific trace.id" /></p>
<h2>Figuring out the active OTel trace and span</h2>
<p>The primary technical challenge in this endeavor is essentially the following: whenever the profiler interrupts an OTel instrumented process to capture a stacktrace, we need to be able to efficiently determine the active span and trace ID (per-thread) and the service name (per-process).</p>
<p>For the purpose of this blog, we'll focus on the recently released <a href="https://github.com/elastic/elastic-otel-java">Elastic distribution of the OTel Java instrumentation</a>, but the approach that we ended up with generalizes to any language that can load and call into a native library. So, how do we get our hands on those IDs?</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/continuous-profiling-distributed-tracing-correlation/service-popout.png" alt="Profiling correlated with service.name, showing  CO2 and cloud cost impact by line of code." /></p>
<p>The OTel Java agent itself keeps track of the active span by storing a stack of spans in the <a href="https://opentelemetry.io/docs/concepts/context-propagation/#context">OpenTelemetryContext</a>, which itself is stored in a <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html">ThreadLocal</a> variable. We originally considered reading these Java structures directly from BPF, but we eventually decided against that approach. There is no documented specification on how ThreadLocals are implemented, and reliably reading and following the JVM's internal data-structures would incur a high maintenance burden. Any minor update to the JVM could change details of the structure layouts. To add to this, we would also have to reverse engineer how each JVM version lays out Java class fields in memory, as well as how all the high-level Java types used in the context objects are actually implemented under the hood. This approach further wouldn't generalize to any non-JVM language and needs to be repeated for any language that we wish to support.</p>
<p>After we had convinced ourselves that reading Java ThreadLocal directly is not the answer, we decided to look for more portable alternatives instead. The option that we ultimately settled with is to load and call into a C++ library that is responsible for making the required information available via a known and defined interface whenever the span changes.</p>
<p>Other than with Java's ThreadLocals, the details on how a native shared library should expose per-process and per-thread data are well-defined in the System V ABI specification and the architecture specific ELF ABI documents.</p>
<h2>Exposing per-process information</h2>
<p>Exposing per-process data is easy: we simply declare a global variable . . .</p>
<pre><code class="language-java">void* elastic_tracecorr_process_storage_v1 = nullptr;
</code></pre>
<p>. . . and expose it via ELF symbols. When the user initializes the OTel library to set the service name, we allocate a buffer and populate it with data in a <a href="https://github.com/elastic/apm/blob/149cd3e39a77a58002344270ed2ad35357bdd02d/specs/agents/universal-profiling-integration.md#process-storage-layout">protocol that we defined for this purpose</a>. Once the buffer is fully populated, we update the global pointer to point to the buffer.</p>
<p>On the profiling agent side, we already have code in place that detects libraries and executables loaded into any process's address space. We normally use this mechanism to detect and analyze high-level language interpreters (e.g., libpython, libjvm) when they are loaded, but it also turned out to be a perfect fit to detect the OTel trace correlation library. When the library is detected in a process, we scan the exports, resolve the symbol, and read the per-process information directly from the instrumented process’ memory.</p>
<h2>Exposing per-thread information</h2>
<p>With the easy part out of the way, let's get to the nitty-gritty portion: exposing per-thread information via thread-local storage (TLS). So, what exactly is TLS, and how does it work? At the most basic level, the idea is to have <strong>one instance of a variable for every thread</strong>. Semantically you can think of it like having a global Map&lt;ThreadID, T&gt;, although that is not how it is implemented.</p>
<p>On Linux, there are two major options for thread locals: TSD and TLS.</p>
<h2>Thread-specific data (TSD)</h2>
<p>TSD is the older and probably more commonly known variant. It works by explicitly allocating a key via pthread_key_create — usually during process startup — and passing it to all threads that require access to the thread-local variable. The threads can then pass that key to the pthread_getspecific and pthread_setspecific functions to read and update the variable for the currently running thread.</p>
<p>TSD is simple, but for our purposes it has a range of drawbacks:</p>
<ul>
<li>
<p>The pthread_key_t structure is opaque and doesn't have a defined layout. Similar to the Java ThreadLocals, the underlying data-structures aren't defined by the ABI documents and different libc implementations (glibc, musl) will handle them differently.</p>
</li>
<li>
<p>We cannot call a function like pthread_getspecific from BPF, so we'd have to reverse engineer and reimplement the logic. Logic may change between libc versions, and we’d have to detect the version and support all variants that may come up in the wild.</p>
</li>
<li>
<p>TSD performance is not predictable and varies depending on how many thread local variables have been allocated in the process previously. This may not be a huge concern for Java specifically since spans are typically not swapped super rapidly, but it’d likely be quite noticeable for user-mode scheduling languages where the context might need to be swapped at every await point/coroutine yield.</p>
</li>
</ul>
<p>None of this is strictly prohibitive, but a lot of this is annoying at the very least. Let’s see if we can do better!</p>
<h2>Thread-local storage (TLS)</h2>
<p>Starting with C11 and C++11, both languages support thread local variables directly via the _Thread_local and thread_local storage specifiers, respectively. Declaring a variable as per-thread is now a matter of simply adding the keyword:</p>
<pre><code class="language-java">thread_local void* elastic_tracecorr_tls_v1 = nullptr;
</code></pre>
<p>You might assume that the compiler simply inserts calls to the corresponding pthread function calls when variables declared with this are accessed, but this is not actually the case. The reality is surprisingly complicated, and it turns out that there are four different models of TLS that the compiler can choose to generate. For some of those models, there are further multiple dialects that can be used to implement them. The different models and dialects come with various portability versus performance trade-offs. If you are interested in the details, I suggest reading this <a href="https://maskray.me/blog/2021-02-14-all-about-thread-local-storage">blog article</a> that does a great job at explaining them.</p>
<p>The TLS model and dialect are usually chosen by the compiler based on a somewhat opaque and complicated set of architecture-specific rules. Fortunately for us, both gcc and clang allow users to pick a particular one using the -ftls-model and -mtls-dialect arguments. The variant that we ended up picking for our purposes is -ftls-model=global-dynamic and -mtls-dialect=gnu2 (and desc on aarch64).</p>
<p>Let's take a look at the assembly that is being generated when accessing a thread_local variable under these settings. Our function:</p>
<pre><code class="language-java">void setThreadProfilingCorrelationBuffer(JNIEnv* jniEnv, jobject bytebuffer) {
  if (bytebuffer == nullptr) {
    elastic_tracecorr_tls_v1 = nullptr;
  } else {
    elastic_tracecorr_tls_v1 = jniEnv-&gt;GetDirectBufferAddress(bytebuffer);
  }
}
</code></pre>
<p>Is compiled to the following assembly code:</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/continuous-profiling-distributed-tracing-correlation/assembly.png" alt="assembly" /></p>
<p>Both possible branches assign a value to our thread-local variable. Let’s focus at the right branch corresponding to the nullptr case to get rid of the noise from the GetDirectBufferAddress function call:</p>
<pre><code class="language-java">lea   rax, elastic_tracecorr_tls_v1_tlsdesc  ;; Load some pointer into rax.
call  qword ptr [rax]                        ;; Read &amp; call function pointer at rax.
mov   qword ptr fs:[rax], 0                  ;; Assign 0 to the pointer returned by
                                             ;; the function that we just called.
</code></pre>
<p>The fs: portion of the mov instruction is the actual magic bit that makes the memory read per-thread. We’ll get to that later; let’s first look at the mysterious elastic_tracecorr_tls_v1_tlsdesc variable that the compiler emitted here. It’s an instance of the tlsdesc structure that is located somewhere in the .got.plt ELF section. The structure looks like this:</p>
<pre><code class="language-java">struct tlsdesc {
  // Function pointer used to retrieve the offset
  uint64_t (*resolver)(tlsdesc*);

  // TLS offset -- more on that later.
  uint64_t tp_offset;
}
</code></pre>
<p>The resolver field is initialized with nullptr and tp_offset with a per-executable offset. The first thread-local variable in an executable will usually have offset 0, the next one sizeof(first_var), and so on. At first glance this may appear to be similar to how TSD works, with the call to pthread_getspecific to resolve the actual offset, but there is a crucial difference. When the library is loaded, the resolver field is filled in with the address of __tls_get_addr by the loader (ld.so). __tls_get_addr is a relatively heavy function that allocates a TLS offset that is globally unique between all shared libraries in the process. It then proceeds by updating the tlsdesc structure itself, inserting the global offset and replacing the resolver function with a trivial one:</p>
<pre><code class="language-java">void* second_stage_resolver(tlsdesc* desc) {
  return tlsdesc-&gt;tp_offset;
}
</code></pre>
<p>In essence, this means that the first access to a tlsdesc based thread-local variable is rather expensive, but all subsequent ones are cheap. We further know that by the time that our C++ library starts publishing per-thread data, it must have gone through the initial resolving process already. Consequently, all that we need to do is to read the final offset from the process's memory and memorize it. We also refresh the offset every now and then to ensure that we really have the final offset, combating the unlikely but possible race condition that we read the offset before it was initialized. We can detect this case by comparing the resolver address against the address of the __tls_get_addr function exported by ld.so.</p>
<h2>Determining the TLS offset from an external process</h2>
<p>With that out of the way, the next question that arises is how to actually find the tlsdesc in memory so that we can read the offset. Intuitively one might expect that the dynamic symbol exported on the ELF file points to that descriptor, but that is not actually the case.</p>
<pre><code class="language-bash">$ readelf --wide --dyn-syms elastic-jvmti-linux-x64.so | grep elastic_tracecorr_tls_v1
328: 0000000000000000 	8 TLS 	GLOBAL DEFAULT   19 elastic_tracecorr_tls_v1
</code></pre>
<p>The dynamic symbol instead contains an offset relative to the start of the .tls ELF section and points to the initial value that libc initializes the TLS value with when it is allocated. So how does ld.so find the tlsdesc to fill in the initial resolver? In addition to the dynamic symbol, the compiler also emits a relocation record for our symbol, and that one actually points to the descriptor structure that we are looking for.</p>
<pre><code class="language-bash">$ readelf --relocs --wide elastic-jvmti-linux-x64.so | grep R_X86_64_TLSDESC
00000000000426e8  0000014800000024 R_X86_64_TLSDESC   	0000000000000000
elastic_tracecorr_tls_v1 + 0
</code></pre>
<p>To read the final TLS offset, we thus simply have to:</p>
<ul>
<li>
<p>Wait for the event notifying us about a new shared library being loaded into a process</p>
</li>
<li>
<p>Do some cheap heuristics to detect our C++ library, avoiding the more expensive analysis below from being executed for every unrelated library on the system</p>
</li>
<li>
<p>Analyze the library on disk and scan ELF relocations for our per-thread variable to extract the tlsdesc address</p>
</li>
<li>
<p>Rebase that address to match where our library was loaded in that particular process</p>
</li>
<li>
<p>Read the offset from tlsdesc+8</p>
</li>
</ul>
<h2>Determining the TLS base</h2>
<p>Now that we have the offset, how do we use that to actually read the data that the library puts there for us? This brings us back to the magic fs: portion of the mov instruction that we discussed earlier. In X86, most memory operands can optionally be supplied with a segment register that influences the address translation.</p>
<p>Segments are an archaic construct from the early days of 16-bit X86 where they were used to extend the address space. Essentially the architecture provides a range of segment registers that can be configured with different base addresses, thus allowing more than 16-bits worth of memory to be accessed. In times of 64-bit processors, this is hardly a concern anymore. In fact, X86-64 aka AMD64 got rid of all but two of those segment registers: fs and gs.</p>
<p>So why keep two of them? It turns out that they are quite useful for the use-case of thread-local data. Since every thread can be configured to have its own base address in these segment registers, we can use it to point to a block of data for this specific thread. That is precisely what libc implementations on Linux are doing with the fs segment. The offset that we snatched from the processes memory earlier is used as an address with the fs segment register, and the CPU automatically adds it to the per-thread base address.</p>
<p>To retrieve the base address pointed to by the fs segment register in the kernel, we need to read its destination from the kernel’s task_struct for the thread that we happened to interrupt with our profiling timer event. Getting the task struct is easy because we are blessed with the bpf_get_current_task BPF helper functions. BPF helpers are pretty much syscalls for BPF programs: we can just ask the Linux kernel to hand us the pointer.</p>
<p>Armed with the task pointer, we now have to read the thread.fsbase (X86-64) or thread.uw.tp_value (aarch64) field to get our desired base address that the user-mode process accesses via fs. This is where things get complicated one last time, at least if we wish to support older kernels without <a href="https://www.kernel.org/doc/html/latest/bpf/btf.html">BTF support</a> (we do!). The <a href="https://github.com/torvalds/linux/blob/259f7d5e2baf87fcbb4fabc46526c9c47fed1914/include/linux/sched.h#L748">task_struct is huge</a> and there are hundreds of fields that can be present or not depending on how the kernel is configured. Being a core primitive of the scheduler, it is also constantly subject to changes between different kernel versions. On modern Linux distributions, the kernel is typically nice enough to tell us the offset via BTF. On older ones, the situation is more complicated. Since hardcoding the offset is clearly not an option if we hope the code to be portable, we instead have to figure out the offset by ourselves.</p>
<p>We do this by consulting /proc/kallsyms, a file with mappings between kernel functions and their addresses, and then using BPF to dump the compiled code of a kernel function that rarely changes and uses the desired offset. We dynamically disassemble and analyze the function and extract the offset directly from the assembly. For X86-64 specifically, we dump the <a href="https://elixir.bootlin.com/linux/v5.9.16/source/arch/x86/kernel/hw_breakpoint.c#L452">aout_dump_debugregs</a> function that accesses thread-&gt;ptrace_bps, which has consistently been 16 bytes away from the fsbase field that we are interested in for all kernels that we have ever looked at.</p>
<h2>Reading TLS data from kernel</h2>
<p>With all the required offsets at our hands, we can now finally do what we set out to do in the first place: use them to enrich our stack traces with the OTel trace and span IDs that our C++ library prepared for us!</p>
<pre><code class="language-java">void maybe_add_otel_info(Trace* trace) {
  // Did user-mode insert a TLS offset for this process? Read it.
  TraceCorrProcInfo* proc = bpf_map_lookup_elem(&amp;tracecorr_procs, &amp;trace-&gt;pid);

  // No entry -&gt; process doesn't have the C++ library loaded.
  if (!proc) return;

  // Load the fsbase offset from our global configuration map.
  u32 key = 0;
  SystemConfig* syscfg = bpf_map_lookup_elem(&amp;system_config, &amp;key);

  // Read the fsbase offset from the kernel's task struct.
  u8* fsbase;
  u8* task = (u8*)bpf_get_current_task();
  bpf_probe_read_kernel(&amp;fsbase, sizeof(fsbase), task + syscfg-&gt;fsbase_offset);

  // Use the TLS offset to read the **pointer** to our TLS buffer.
  void* corr_buf_ptr;
  bpf_probe_read_user(
    &amp;corr_buf_ptr,
    sizeof(corr_buf_ptr),
    fsbase + proc-&gt;tls_offset
  );

  // Read the information that our library prepared for us.
  TraceCorrelationBuf corr_buf;
  bpf_probe_read_user(&amp;corr_buf, sizeof(corr_buf), corr_buf_ptr);

  // If the library reports that we are currently in a trace, store it into
  // the stack trace that will be reported to our user-land process.
  if (corr_buf.trace_present &amp;&amp; corr_buf.valid) {
    trace-&gt;otel_trace_id.as_int.hi = corr_buf.trace_id.as_int.hi;
    trace-&gt;otel_trace_id.as_int.lo = corr_buf.trace_id.as_int.lo;
    trace-&gt;otel_span_id.as_int = corr_buf.span_id.as_int;
  }
}
</code></pre>
<h2>Sending out the mappings</h2>
<p>From this point on, everything further is pretty simple. The C++ library sets up a unix datagram socket during startup and communicates the socket path to the profiler via the per-process data block. The stacktraces annotated with the OTel trace and span IDs are sent from BPF to our user-mode profiler process via perf event buffers, which in turn sends the mappings between OTel span and trace and stack trace hashes to the C++ library. Our extensions to the OTel instrumentation framework then read those mappings and insert the stack trace hashes into the OTel trace.</p>
<p>This approach has a few major upsides compared to the perhaps more obvious alternative of sending out the OTel span and trace ID with the profiler’s stacktrace records. We want the stacktrace associations to be stored in the trace indices to allow filtering and aggregating stacktraces by the plethora of fields available on OTel traces. If we were to send out the trace IDs via the profiler's gRPC connection instead, we’d have to search for and update the corresponding OTel trace records in the profiling collector to insert the stack trace hashes.</p>
<p>This is not trivial: stacktraces are sent out rather frequently (every 5 seconds, as of writing) and the corresponding OTel trace might not have been sent and stored by the time the corresponding stack traces arrive in our cluster. We’d have to build a kind of delay queue and periodically retry updating the OTel trace documents, introducing avoidable database work and complexity in the collectors. With the approach of sending stacktrace mappings to the OTel instrumented process instead, the need for server-side merging vanishes entirely.</p>
<h2>Trace correlation in action</h2>
<p>With all the hard work out of the way, let’s take a look at what trace correlation looks like in action!</p>
&lt;Video vidyardUuid=&quot;JYTzQYeiJ6CK6K3hZ33sz5&quot; /&gt;
<h2>Future work: Supporting other languages</h2>
<p>We have demonstrated that trace correlation can work nicely for Java, but we have no intention of stopping there. The general approach that we discussed previously should work for any language that can efficiently load and call into our C++ library and doesn’t do user-mode scheduling with coroutines. The problem with user-mode scheduling is that the logical thread can change at any await/yield point, requiring us to update the trace IDs in TLS. Many such coroutine environments like Rust’s Tokio provide the ability to register a callback for whenever the active task is swapped, so they can be supported easily. Other languages, however, do not provide that option.</p>
<p>One prominent example in that category is Go: goroutines are built on user-mode scheduling, but to our knowledge there’s no way to instrument the scheduler. Such languages will need solutions that don’t go via the generic TLS path. For Go specifically, we have already built a prototype that uses pprof labels that are associated with a specific Goroutine, having Go’s scheduler update them for us automatically.</p>
<h2>Getting started</h2>
<p>We hope this blog post has given you an overview of correlating profiling signals to distributed tracing, and its benefits for end-users.</p>
<p>To get started, download the <a href="https://github.com/elastic/elastic-otel-java">Elastic distribution of the OTel agent</a>, which contains the new trace correlation library. Additionally, you will need the latest version of Universal Profiling agent, bundled with <a href="https://www.elastic.co/blog/whats-new-elastic-8-13-0">Elastic Stack version 8.13</a>.</p>
<h2>Acknowledgment</h2>
<p>We appreciate <a href="https://github.com/trask">Trask Stalnaker</a>, maintainer of the OTel Java agent, for his feedback on our approach and for reviewing the early draft of this blog post.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/continuous-profiling-distributed-tracing-correlation/Under_highway_bridge.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[OpenTelemetry and Elastic: Working together to establish continuous profiling for the community]]></title>
            <link>https://www.elastic.co/observability-labs/blog/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry</link>
            <guid isPermaLink="false">elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry</guid>
            <pubDate>Tue, 12 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[OpenTelemetry is embracing profiling. Elastic is donating its whole-system continuous profiling agent to OpenTelemetry to further this advancement, empowering OTel users to improve computational efficiency and reduce their carbon footprint.]]></description>
            <content:encoded><![CDATA[<p>Profiling is emerging as a core pillar of observability, aptly dubbed the fourth pillar, with the OpenTelemetry (OTel) project leading this essential development. This blog post dives into the recent advancements in profiling within OTel and how Elastic® is actively contributing toward it.</p>
<p>At Elastic, we’re big believers in and contributors to the OpenTelemetry project. The project’s benefits of flexibility, performance, and vendor agnosticism have been making their rounds; we’ve seen a groundswell of customer interest.</p>
<p>To this end, after donating our <a href="https://www.elastic.co/blog/ecs-elastic-common-schema-otel-opentelemetry-faq"><strong>Elastic Common Schema</strong></a> and our <a href="https://www.elastic.co/blog/elastic-invokedynamic-opentelemetry-java-agent">invokedynamic based java agent approach</a>, we recently <a href="https://github.com/open-telemetry/community/issues/1918">announced our intent to donate our continuous profiling agent</a> — a whole-system, always-on, continuous profiling solution that eliminates the need for run-time/bytecode instrumentation, recompilation, on-host debug symbols, or service restarts.</p>
<p>Profiling helps organizations run efficient services by minimizing computational wastage, thereby reducing operational costs. Leveraging <a href="https://ebpf.io/">eBPF</a>, the Elastic profiling agent provides unprecedented visibility into the runtime behavior of all applications: it builds stacktraces that go from the kernel, through userspace native code, all the way into code running in higher level runtimes, enabling you to identify performance regressions, reduce wasteful computations, and debug complex issues faster.</p>
<h2>Enabling profiling in OpenTelemetry: A step toward unified observability</h2>
<p>Elastic actively participates in the OTel community, particularly within the Profiling Special Interest Group (SIG). This group has been instrumental in defining the OTel <a href="https://github.com/open-telemetry/oteps/blob/main/text/profiles/0239-profiles-data-model.md">Profiling Data Model</a>, a crucial step toward standardizing profiling data.</p>
<p>The recent merger of the <a href="https://github.com/open-telemetry/oteps/pull/239">OpenTelemetry Enhancement Proposal (OTEP) introducing profiling support to the OpenTelemetry Protocol (OTLP)</a> marks a significant milestone. With the standardization of profiles as a core observability pillar alongside metrics, tracing, and logs, OTel offers a comprehensive suite of observability tools, empowering users to gain a holistic view of their applications' health and performance.</p>
<p>In line with this advancement, we are donating our whole-system, eBPF-based continuous profiling agent to OTel. In parallel, we are implementing the experimental OTel Profiling signal in the profiling agent, to ensure and demonstrate OTel protocol compatibility in the agent and prepare it for a fully OTel-based collection of profiling signals and correlate it to logs, metrics, and traces.</p>
<h2>Why is Elastic donating the eBPF-based profiling agent to OpenTelemetry?</h2>
<p>Computational efficiency has always been a critical concern for software professionals. However, in an era where every line of code affects both the bottom line and the environment, there's an additional reason to focus on it. Elastic is committed to helping the OpenTelemetry community enhance computational efficiency because efficient software not only reduces the cost of goods sold (COGS) but also reduces carbon footprint.</p>
<p>We have seen firsthand — both internally and from our customers' testimonials — how profiling insights aid in enhancing software efficiency. This results in an improved customer experience, lower resource consumption, and reduced cloud costs.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry/1-flamegraph.png" alt="A differential flamegraph showing regression in release comparison" /></p>
<p>Moreover, adopting a whole-system profiling strategy, such as <a href="https://www.elastic.co/blog/whole-system-visibility-elastic-universal-profiling">Elastic Universal Profiling</a>, differs significantly from traditional instrumentation profilers that focus solely on runtime. Elastic Universal Profiling provides whole-system visibility, profiling not only your own code but also third-party libraries, kernel operations, and other code you don't own. This comprehensive approach facilitates rapid optimizations by identifying non-optimal common libraries and uncovering &quot;unknown unknowns&quot; that consume CPU cycles. Often, a tipping point is reached when the resource consumption of libraries or certain daemon processes exceeds that of the applications themselves. Without system-wide profiling, along with the capabilities to slice data per service and aggregate total usage, pinpointing these resource-intensive components becomes a formidable challenge.</p>
<p>At Elastic, we have a customer with an extensive cloud footprint who plans to negotiate with their cloud provider to reclaim money for the significant compute resource consumed by the cloud provider's in-VM agents. These examples highlight the importance of whole-system profiling and the benefits that the OpenTelemetry community will gain if the donation proposal is accepted.</p>
<p>Specifically, OTel users will gain access to a lightweight, battle-tested production-grade continuous profiling agent with the following features:</p>
<ul>
<li>
<p>Very low CPU and memory overhead (1% CPU and 250MB memory are our upper limits in testing, and the agent typically manages to stay way below that)</p>
</li>
<li>
<p>Support for native C/C++ executables without the need for DWARF debug information by leveraging .eh_frame data, as described in “<a href="https://www.elastic.co/blog/universal-profiling-frame-pointers-symbols-ebpf">How Universal Profiling unwinds stacks without frame pointers and symbols</a>”</p>
</li>
<li>
<p>Support profiling of system libraries without frame pointers and without debug symbols on the host</p>
</li>
<li>
<p>Support for mixed stacktraces between runtimes — stacktraces go from Kernel space through unmodified system libraries all the way into high-level languages</p>
</li>
<li>
<p>Support for native code (C/C++, Rust, Zig, Go, etc. without debug symbols on host)</p>
</li>
<li>
<p>Support for a broad set of High-level languages (Hotspot JVM, Python, Ruby, PHP, Node.JS, V8, Perl), .NET is in preparation</p>
</li>
<li>
<p><strong>100% non-intrusive:</strong> there's no need to load agents or libraries into the processes that are being profiled</p>
</li>
<li>
<p>No need for any reconfiguration, instrumentation, or restarts of HLL interpreters and VMs: the agent supports unwinding each of the supported languages in the default configuration</p>
</li>
<li>
<p>Support for x86 and Arm64 CPU architectures</p>
</li>
<li>
<p>Support for native inline frames, which provide insights into compiler optimizations and offer a higher precision of function call chains</p>
</li>
<li>
<p>Support for <a href="https://www.elastic.co/guide/en/observability/current/profiling-probabilistic-profiling.html">Probabilistic Profiling</a> to reduce data storage costs</p>
</li>
<li>
<p>. . . and more</p>
</li>
</ul>
<p>Elastic's commitment to enhancing computational efficiency and our belief in the OpenTelemetry vision underscores our dedication to advancing the observability ecosystem –– by donating the profiling agent. Elastic is not only contributing technology but also dedicating a team of specialized profiling domain experts to co-maintain and advance the profiling capabilities within OpenTelemetry.</p>
<h2>How does this donation benefit the OTel community?</h2>
<p>Metrics, logs, and traces offer invaluable insights into system health. But what if you could unlock an even deeper level of visibility? Here's why profiling is a perfect complement to your OTel toolkit:</p>
<h3>1. Deep system visibility: Beyond the surface</h3>
<p>Think of whole-system profiling as an MRI scan for your fleet. It goes deeper into the internals of your system, revealing hidden performance issues lurking beneath the surface. You can identify &quot;unknown unknowns&quot; — inefficiencies you wouldn't have noticed otherwise — and gain a comprehensive understanding of how your system functions at its core.</p>
<h3>2. Cross-signal correlation: Answering &quot;why&quot; with confidence</h3>
<p>The Elastic Universal Profiling agent supports trace correlation with the OTel Java agent/SDK (with Go support coming soon!). This correlation enables OTel users to view profiling data by services or service endpoints, allowing for a more context-aware and targeted root cause analysis. This powerful combination allows you to pinpoint the exact cause of resource consumption at the trace level. No more guessing why specific functions hog CPU or why certain events occur. You can finally answer the critical &quot;why&quot; questions with precision, enabling targeted optimization efforts.</p>
<h3>3. Cost and sustainability optimization: Beyond performance</h3>
<p>Our approach to profiling goes beyond just performance gains. By correlating whole-system profiling data with tracing, we can help you measure the environmental impact and cloud cost associated with specific services and functionalities within your application. This empowers you to make data-driven decisions that optimize both performance and resource utilization, leading to a more sustainable and cost-effective operation.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry/2-universal-profiling.png" alt="A differential function insight, showing the performance, cost, and CO2 impact of a change" /></p>
<h2>Elastic's commitment to OpenTelemetry</h2>
<p>Elastic currently supports a growing list of Cloud Native Computing Foundation (CNCF) projects <a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">such as Kubernetes (K8S), Prometheus, Fluentd, Fluent Bit, and Istio</a>. <a href="https://www.elastic.co/observability/application-performance-monitoring">Elastic’s application performance monitoring (APM)</a> also natively supports OTel, ensuring all APM capabilities are available with either Elastic or OTel agents or a combination of the two. In addition to the ECS contribution and ongoing collaboration with OTel SemConv, Elastic <a href="https://www.elastic.co/observability/opentelemetry">has continued to make contributions to other OTel projects</a>, including language SDKs (such as OTel Swift, OTel Go, OTel Ruby, and others), and participates in several <a href="https://github.com/open-telemetry/community#special-interest-groups">special interest groups (SIGs)</a> to establish OTel as a standard for observability and security.</p>
<p>We are excited about our <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">strengthening relationship with OTel</a> and the opportunity to donate our profiling agent in a way that benefits both the Elastic community and the broader OTel community.Learn more about <a href="https://www.elastic.co/observability/opentelemetry">Elastic’s OpenTelemetry support</a> or contribute to the <a href="https://github.com/open-telemetry/community/issues/1918">donation proposal or just join the conversation</a>.</p>
<p>Stay tuned for further updates as the profiling part of OTel continues to evolve.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry/ecs-otel-announcement-1.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Elastic Universal Profiling agent, a continuous profiling solution, is now open source]]></title>
            <link>https://www.elastic.co/observability-labs/blog/elastic-universal-profiling-agent-open-source</link>
            <guid isPermaLink="false">elastic-universal-profiling-agent-open-source</guid>
            <pubDate>Mon, 15 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[At Elastic, open source isn't just philosophy, it's our DNA. Dive into the future with our open-sourced Universal Profiling agent, revolutionizing software efficiency and sustainability.]]></description>
            <content:encoded><![CDATA[<p>Elastic Universal Profiling™ agent is now open source! The industry’s most advanced fleetwide continuous profiling solution empowers users to identify performance bottlenecks, reduce cloud spend, and minimize their carbon footprint. This post explores the history of the agent, its move to open source, and its future integration with OpenTelemetry.</p>
<h2>Elastic Universal Profiling™ Agent goes open source under Apache 2</h2>
<p>At Elastic, open source is more than just a philosophy — it's our DNA. We believe the benefits of whole-system continuous profiling extend far beyond performance optimization. It's a win for businesses and the planet alike. For instance, since launching Elastic Universal Profiling in general availability (GA), we've observed a wide variety of use cases from customers.</p>
<p>These range from customers relying fully on Universal Profiling's <a href="https://www.elastic.co/guide/en/observability/current/universal-profiling.html#profiling-differential-views-intro">differential flame graphs and topN functions</a> for insights during release management to utilizing AI assistants for quickly optimizing expensive functions. This includes using profiling data to identify the optimal energy-efficient cloud region to run certain workloads. Additionally, customers are using insights that Universal Profiling provides to build evidence to challenge cloud provider bills. As it turns out, cloud providers' in-VM agents can consume a significant portion of the CPU time, which customers are billed for.</p>
<p>In a move that will empower the community to take advantage of continuous profiling's benefits, <strong>we're thrilled to announce that the Elastic Universal Profiling agent</strong> , a pioneering eBPF-based continuous profiling agent, <strong>is now open source under the Apache 2 license!</strong></p>
<p>This move democratizes <strong>hyper-scaler efficiency for everyone</strong> , opening exciting new possibilities for the future of continuous profiling, as well as its role in observability and <strong>OpenTelemetry</strong>.</p>
<h2>Implementation of the OpenTelemetry (OTel) Profiling protocol</h2>
<p>Our commitment to open source goes beyond just the agent itself. We recently <a href="https://www.elastic.co/blog/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry">announced our intent to donate</a> the agent to OpenTelemetry and have further solidified this goal by implementing the experimental <a href="https://github.com/open-telemetry/oteps/blob/main/text/profiles/0239-profiles-data-model.md">OTel Profiling data model</a>. This allows the open-sourced eBPF-based continuous profiling agent to communicate seamlessly with OpenTelemetry backends.</p>
<p>But that's not all! We've also launched an innovative feature that <a href="https://www.elastic.co/blog/continuous-profiling-distributed-tracing-correlation">correlates profiling data with OpenTelemetry distributed traces</a>. This powerful capability offers a deeper level of insight into application performance, enabling the identification of bottlenecks with greater precision. Upon donating the Profiling agent to OTel, Elastic will also contribute critical components that enable distributed trace correlation within the <a href="https://github.com/elastic/elastic-otel-java">Elastic distribution of the OTel Java agent</a> to the upstream OTel Java SDK. This underscores Elastic Observability's commitment to both open source and the support of open standards like OpenTelemetry while pushing the boundaries of what is possible in observability.</p>
<h2>What does this mean for Elastic Universal Profiling customers?</h2>
<p>We'd like to express our <strong>immense gratitude to all our customers</strong> who have been part of this journey, from the early stages of private beta to GA. Your feedback has been invaluable in shaping Universal Profiling into the powerful product it is today.</p>
<p>By open-sourcing the Universal Profiling agent and contributing it to OpenTelemetry, we're fostering a win-win situation for both you and the broader community. This move opens doors for innovation and collaboration, ultimately leading to a more robust and versatile whole-system continuous profiling solution for everyone.</p>
<p>Furthermore, we're actively working on exciting novel ways to integrate Universal Profiling seamlessly within Elastic Observability. Expect further announcements soon, outlining how you can unlock even greater value from your profiling data within a unified observability experience in a way that has never been done before.</p>
<p>The open-sourced agent is using the recently released (experimental) OTel Profiling <a href="https://github.com/open-telemetry/opentelemetry-proto/pull/534">signal</a>. As a precaution, we recommend not using it in production environments.</p>
<p>Please continue using the official Elastic distribution of the Universal Profiling agent until the agent is formally accepted by OTel and the protocol reaches a stable phase. There's no need to take any action at this time, and we will ensure to have a smooth transition plan in place for you.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-universal-profiling-agent-open-source/image1.png" alt="1 - Elastic Universal Profiling" /></p>
<h2>What does this mean for the OpenTelemetry community?</h2>
<p>OpenTelemetry is adopting continuous profiling as a key signal. By open-sourcing the eBPF-based profiling agent and working towards donating it to OTel, Elastic is making it possible to accelerate the standardization of continuous profiling within OpenTelemetry. This move has a massive impact on the observability community, empowering everyone to continuously profile their systems with a standardized protocol.</p>
<p>This is particularly timely as <a href="https://www.bbc.co.uk/news/technology-32335003">Moore's Law</a> slows down and cloud computing takes hold, making computational efficiency critical for businesses.</p>
<p>Here's how whole-system continuous profiling benefits you:</p>
<ul>
<li>
<p><strong>Maximize gross margins:</strong> By reducing the computational resources needed to run applications, businesses can optimize their cloud spend and improve profitability. Whole-system continuous profiling is one way of identifying the most expensive applications (down to the lines of code) across diverse environments that may span multiple cloud providers. This principle aligns with the familiar adage, <em>&quot;a penny saved is a penny earned.&quot;</em> In the cloud context, every CPU cycle saved translates to money saved.</p>
</li>
<li>
<p><strong>Minimize environmental impact:</strong> Energy consumption associated with computing is a growing concern (source: <a href="https://energy.mit.edu/news/energy-efficient-computing/">MIT Energy Initiative</a>). More efficient code translates to lower energy consumption, contributing to a reduction in carbon footprint.</p>
</li>
<li>
<p><strong>Accelerate engineering workflows:</strong> Continuous profiling provides detailed insights to help debug complex issues faster, guide development, and improve overall code quality.</p>
</li>
</ul>
<p>This is where Elastic Universal Profiling comes in — designed to help organizations run efficient services by minimizing computational wastage. To this end, it measures code efficiency in three dimensions: <strong>CPU utilization</strong> , <strong>CO</strong>** 2 <strong>, and</strong> cloud cost**.</p>
<p>Elastic's journey with continuous profiling began by joining forces with <a href="https://www.elastic.co/about/press/elastic-and-optimyze-join-forces-to-deliver-continuous-profiling-of-infrastructure-applications-and-services">optimyze.cloud</a> –– this became the foundation for <a href="https://www.elastic.co/observability/universal-profiling">Elastic Universal Profiling</a>. We are excited to see this product evolve into its next growth phase in the open-source world.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-universal-profiling-agent-open-source/image2.png" alt="2 - car manufacturers" /></p>
<h2>Ready to give it a spin?</h2>
<p>As Elastic Universal Profiling transitions into this new open source era, the potential for transformative impact on performance optimization, cost efficiency, and environmental sustainability is immense. Elastic's approach — balancing innovation with responsibility — paves the way for a future where technology not only powers our world but does so in a way that is sustainable and accessible to all.</p>
<p>Get started with the open source Elastic Universal Profiling agent today! <a href="https://github.com/elastic/otel-profiling-agent/">Download it directly from GitHub</a> and follow the instructions in the repository.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-universal-profiling-agent-open-source/image3.png" alt="3 - dripping graph and data" /></p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/elastic-universal-profiling-agent-open-source/tree_tunnel.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Unlocking whole-system visibility with Elastic Universal Profiling™]]></title>
            <link>https://www.elastic.co/observability-labs/blog/whole-system-visibility-elastic-universal-profiling</link>
            <guid isPermaLink="false">whole-system-visibility-elastic-universal-profiling</guid>
            <pubDate>Mon, 25 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Visual profiling data can be overwhelming. This blog post aims to demystify continuous profiling and guide you through its unique visualizations. We will equip you with the knowledge to derive quick, actionable insights from Universal Profiling™.]]></description>
            <content:encoded><![CDATA[<h2>Identify, optimize, measure, repeat!</h2>
<p>SREs and developers who want to maintain robust, efficient systems and achieve optimal code performance need effective tools to measure and improve code performance. Profilers are invaluable for these tasks, as they can help you boost your app's throughput, ensure consistent system reliability, and gain a deeper understanding of your code's behavior at runtime. However, traditional profilers can be cumbersome to use, as they often require code recompilation and are limited to specific languages. Additionally, they can also have a high overhead that negatively affects performance and makes them less suitable for quick, real-time debugging in production environments.</p>
<p>To address the limitations of traditional profilers, Elastic&lt;sup&gt;®&lt;/sup&gt; recently <a href="https://www.elastic.co/blog/continuous-profiling-is-generally-available">announced the general availability of Elastic Universal Profiling</a>, a <a href="https://www.elastic.co/observability/universal-profiling">continuous profiling</a> product that is refreshingly straightforward to use, eliminating the need for instrumentation, recompilations, or restarts. Moreover, Elastic Universal Profiling does not require on-host debug symbols and is language-agnostic, allowing you to profile any process running on your machines — from your application's code to third-party libraries and even kernel functions.</p>
<p>However, even the most advanced tools require a certain level of expertise to interpret the data effectively. The wealth of visual profiling data — flamegraphs, stacktraces, or functions — can initially seem overwhelming. This blog post aims to demystify <a href="https://www.elastic.co/observability/universal-profiling">continuous profiling</a> and guide you through its unique visualizations. We will equip you with the knowledge to derive quick, actionable insights from Universal Profiling.</p>
<p>Let’s begin.</p>
<h2>Stacktraces: The cornerstone for profiling</h2>
<h3>It all begins with a stacktrace — a snapshot capturing the cascade of function calls.</h3>
<p>A stacktrace is a snapshot of the call stack of an application at a specific point in time. It captures the sequence of function calls that the program has made up to that point. In this way, a stacktrace serves as a historical record of the call stack, allowing you to trace back the steps that led to a particular state in your application.</p>
<p>Further, stacktraces are the foundational data structure that profilers rely on to determine what an application is executing at any given moment. This is particularly useful when, for instance, your infrastructure monitoring indicates that your application servers are consuming 95% of CPU resources. While utilities such as 'top -H' can show the top processes that are consuming CPU, they lack the granularity needed to identify the specific lines of code (in the top process) responsible for the high usage.</p>
<p>In the case of Elastic Universal Profiling, <a href="https://www.elastic.co/blog/ebpf-observability-security-workload-profiling">eBPF is used</a> to perform sampling of every process that is keeping a CPU core busy. Unlike most instrumentation profilers that focus solely on your application code, Elastic Universal Profiling provides whole-system visibility — it profiles not just your code, but also code you don't own, including third-party libraries and even kernel operations.</p>
<p>The diagram below shows how the Universal Profiling agent works at a very high level. Step 5 indicates the ingestion of the stacktraces into the profiling collector, a new part of the Elastic Stack.</p>
<p>_ <strong>Just</strong> _ <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">_ <strong>deploy the profiling host agent</strong> _</a> <em><strong>and receive profiling data (in Kibana</strong></em>&lt;sup&gt;&lt;em&gt;®&lt;/em&gt;&lt;/sup&gt;<em><strong>) a few minutes later.</strong></em> <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">_ <strong>Get started now</strong> _</a>_ <strong>.</strong> _</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/elastic-blog-1-flowchart-linux.png" alt="High-level depiction of how the profiling agent works" /></p>
<ol>
<li>
<p>Unwinder eBPF programs (bytecode) are sent to the kernel.</p>
</li>
<li>
<p>The kernel verifies that the BPF program is safe. If accepted, the program is attached to the probes and executed when the event occurs.</p>
</li>
<li>
<p>The eBPF programs pass the collected data to userspace via maps.</p>
</li>
<li>
<p>The agent reads the collected data from maps. The data transferred from the agent to the maps are process-specific and interpreter-specific meta-information that help the eBPF unwinder programs perform unwinding.</p>
</li>
<li>
<p>Stacktraces, metrics, and metadata are pushed to the Elastic Stack.</p>
</li>
<li>
<p>Visualize data as flamegraphs, stacktraces, and functions via Kibana.</p>
</li>
</ol>
<p>While stacktraces are the key ingredient for most profiling tools, interpreting them can be tricky. Let's take a look at a simple example to make things a bit easier. The table below shows a group of stacktraces from a Java application and assigns each a percentage to indicate its share of CPU time consumption.</p>
<p><strong>Table 1: Grouped Stacktraces with CPU Time Percentage</strong></p>
<table>
<thead>
<tr>
<th>Percentage</th>
<th>Function Calls</th>
</tr>
</thead>
<tbody>
<tr>
<td>60%</td>
<td>startApp -&gt; authenticateUser -&gt; processTransaction</td>
</tr>
<tr>
<td>20%</td>
<td>startApp -&gt; loadAccountDetails -&gt; fetchRecentTransactions</td>
</tr>
<tr>
<td>10%</td>
<td>startApp -&gt; authenticateUser -&gt; processTransaction -&gt; verifyFunds</td>
</tr>
<tr>
<td>2%</td>
<td>startApp -&gt; authenticateUser -&gt; processTransaction -&gt;libjvm.so</td>
</tr>
<tr>
<td>1%</td>
<td>startApp -&gt; authenticateUser -&gt; processTransaction -&gt;libjvm.so -&gt;vmlinux: asm_common_interrupt -&gt;vmlinux: asm_sysvec_apic_timer_interrupt</td>
</tr>
</tbody>
</table>
<p>The percentages above represent the relative frequency of each specific stacktrace compared to the total number of stacktraces collected over the observation period, not actual CPU usage percentages. Also, the libjvm.so and kernel frames (vmlinux:*) in the example are commonly observed with whole-system profilers like Elastic Universal Profiling.</p>
<p>Also, we can see that <strong>60%</strong> of the time is spent in the sequence startApp; authenticateUser; processTransaction. An additional <strong>10%</strong> of the processing time is allocated to verifyFunds, a function invoked by processTransaction. Given these observations, it becomes evident that optimization initiatives would yield the most impact if centered on the processTransaction function, as it is one of the most expensive functions. However, real-world stacktraces can be far more intricate than this example. So how do we make sense of them quickly? The answer to this problem resulted in the creation of flamegraphs.</p>
<h2>Flamegraphs: A visualization of stacktraces</h2>
<p>While the above example may appear straightforward, it scarcely reflects the complexities encountered when aggregating multiple stacktraces across a fleet of machines on a continuous basis. The depth of the stack traces and the numerous branching paths can make it increasingly difficult to pinpoint where code is consuming resources. This is where flamegraphs, a concept popularized by <a href="https://www.brendangregg.com/flamegraphs.html">Brendan Gregg</a>, come into play.</p>
<p>A flamegraph is a visual interpretation of stacktraces, designed to quickly and accurately identify the functions that are consuming the most resources. Each function is represented by a rectangle, where the width of the rectangle represents the amount of time spent in the function, and the number of stacked rectangles represents the stack depth. The stack depth is the number of functions that were called to reach the current function.</p>
<p>Elastic Universal Profiling uses icicle graphs, which is an inverted variant of the standard flamegraph. In an icicle graph, the root function is at the top, and its child functions are shown below their parents –– making it easier to see the hierarchy of functions and how they are related to each other.</p>
<p>In most flamegraphs, the y-axis represents stack depth, but there is no standardization for the x-axis. Some profiling tools use the x-axis to indicate the passage of time; in these instances, the graph is more accurately termed a flame chart. Others sort the x-axis alphabetically. Universal Profiling sorts functions on the x-axis based on relative CPU percentage utilization, starting with the function that consumes the most CPU time on the left, as shown in the example icicle graph below.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/elastic-blog-2-cpu-time.png" alt="Example icicle graph: The percentage represents relative CPU time, not the real CPU usage time. " /></p>
<h2>Debugging and optimizing performance issues: Stacktraces, TopN functions, flamegraphs</h2>
<p>SREs and SWEs can use Universal Profiling for troubleshooting, debugging, and performance optimization. It builds stacktraces that go from the kernel, through userspace native code, all the way into code running in higher level runtimes, enabling you to <strong>identify performance regressions</strong> , <strong>reduce wasteful computations</strong> , and <strong>debug complex issues faster</strong>.</p>
<p>To this end, Universal Profiling offers three main visualizations: Stacktraces, TopN Functions, and flamegraphs.</p>
<h3>Stacktrace view</h3>
<p>The stacktraces view shows grouped stacktrace graphs by threads, hosts, Kubernetes deployments, and containers. It can be used to detect unexpected CPU spikes across threads and drill down into a smaller time range to investigate further with a flamegraph. Refer to the <a href="https://www.elastic.co/guide/en/observability/current/universal-profiling.html#profiling-stacktraces-intro">documentation</a> for details.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/elastic-blog-3-wave-patterns.png" alt="Notice the wave pattern in the stacktrace view, enabling you to drill down into a CPU spike " /></p>
<h3>TopN functions view</h3>
<p>Universal Profiling's topN functions view shows the most frequently sampled functions, broken down by CPU time, annualized CO&lt;sub&gt;2&lt;/sub&gt;, and annualized cost estimates. You can use this view to identify the most expensive functions across your entire fleet, and then apply filters to focus on specific components for a more detailed analysis. Clicking on a function name will redirect you to the flamegraph, enabling you to examine the call hierarchy.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/elastic-blog-4-topN-functions-page.png" alt="TopN functions page" /></p>
<h3>Flamegraphs view</h3>
<p>The flamegraph page is where you will most likely spend the most time, especially when debugging and optimizing. We recommend that you use the guide below to identify performance bottlenecks and optimization opportunities with flamegraphs. The three key elements-conditions to look for are <strong>width</strong> , <strong>hierarchy</strong> , and <strong>height</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/elastic-blog-5-icivle-flamegraph.png" alt="Icicle flamegraph: We use the colors to determine different types of code (e.g., native, interpreted, kernel)." /></p>
<p><strong>Width matters:</strong> In icicle graphs, wider rectangles signify functions taking up more CPU time. Always read the graph from left to right and note the widest rectangles, as these are the prime hot spots.</p>
<p><strong>Hierarchy matters:</strong> Navigate the graph's stack to understand function relationships. This vertical examination will help you identify whether one or multiple functions are responsible for performance bottlenecks. This could also uncover opportunities for code improvements, such as swapping an inefficient library or avoiding unnecessary I/O operations.</p>
<p><strong>Height matters:</strong> Elevated or tall stacks in the graph usually point to deep call hierarchies. These can be an indicator of complex and less efficient code structures that may require attention.</p>
<p>Also, when navigating a flamegraph, you may want to look for specific function names to validate your assumptions on their presence: in the Universal Profiling flamegraphs view, there is a “Search” bar at the bottom left corner of the view. You can input a regex, and the match will be highlighted in the flamegraph; by clicking on the left and right arrows next to the Search bar, you can move across the occurrences on the flamegraph and spot callers and callee of the matched function.</p>
<p>In summary,</p>
<ul>
<li><strong>Scan</strong> horizontally from left to right, focusing on width for CPU-intensive functions.</li>
<li><strong>Examine</strong> vertically to examine the stack and spot bottlenecks.</li>
<li><strong>Look</strong> for <strong>towering stacks</strong> to identify potential complexities in the code.</li>
</ul>
<p>To recap, use topN functions to generate optimization hypotheses and validate them with stacktraces and/or flamegraphs. Use stacktraces to monitor CPU utilization trends and to delve into the finer details. Use flamegraphs to quickly debug and optimize your code, using width, hierarchy, and height as guides.</p>
<p>_ <strong>Identify. Optimize. Measure. Repeat!</strong> _</p>
<h2>Measure the impact of your change</h2>
<h3>For the very first time in history, developers can now measure the performance (gained or lost), cloud cost, and carbon footprint impact of every deployed change.</h3>
<p>Once you have identified a performance issue and applied fixes or optimizations to your code, it is essential to measure the impact of your changes. The differential topN functions and differential flamegraph pages are invaluable for this, as they can help you identify regressions and measure your change impact not only in terms of performance but also in terms of carbon emissions and cost savings.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/elastic-blog-6-uni-profiling.png" alt="A differential function view, showing the performance, CO2, and cost impact of a change" /></p>
<p>The Diff column indicates a change in the function’s rank.</p>
<p>You may need to use tags or other metadata, such as container and deployment name, in combination with time ranges to differentiate between the optimized and non-optimized changes.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/elastic-blog-7-differential-flamegraph.png" alt="A differential flamegraph showing regression in A/B testing" /></p>
<h2>Universal Profiling: The key to optimizing application resources</h2>
<p>Computational efficiency is no longer just a nice-to-have, but a must-have from both a financial and environmental sustainability perspective. Elastic Universal Profiling provides unprecedented visibility into the runtime behavior of all your applications, so you can identify and optimize the most resource-intensive areas of your code. The result is not merely better-performing software but also reduced resource consumption, lower cloud costs, and a reduction in carbon footprint. Optimizing your code with Universal Profiling is not only the right thing to do for your business, it’s the right thing to do for our world.</p>
<p><a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">Get started</a> with Elastic Universal Profiling today.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/whole-system-visibility-elastic-universal-profiling/universal-profiling-blog-720x420.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>