<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>The Mainframe Demystified</title>
    <description>Igor Todorovski&apos;s Journey into z/OS</description>
    <link>https://igortodorovskibm.github.io/blog/</link>
    <atom:link href="https://igortodorovskibm.github.io/blog/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Mon, 27 Apr 2026 20:00:37 +0000</pubDate>
    <lastBuildDate>Mon, 27 Apr 2026 20:00:37 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>From Porting to RAG: Building a Vector Search Engine for z/OS</title>
        <description>&lt;p&gt;In a &lt;a href=&quot;https://igortodorovskiibm.github.io/blog/2023/08/22/llama-cpp/&quot;&gt;previous blog post&lt;/a&gt;, we proved that running a 7B parameter LLM on z/OS was possible. It was a milestone, but performance made it more of a curiosity than a real thing. The real question was no longer whether it could run, but whether it could solve a problem worth solving. On z/OS, that doesn’t always mean generating text. Often, it means retrieving the right context at the right moment like helping system admins triage the thousands of messages streaming across the console and surface the ones that actually matter.&lt;/p&gt;

&lt;p&gt;That makes &lt;strong&gt;Retrieval-Augmented Generation (RAG)&lt;/strong&gt; a great fit for z/OS. It works within the platform’s performance limits and respects air-gapped environments. By indexing data locally using efficient embedding models, we can achieve fast semantic search results, turning a slow “curiosity” into a practical, real-time RAG tool.&lt;/p&gt;

&lt;p&gt;This blog introduces &lt;strong&gt;&lt;a href=&quot;https://igortodorovskiibm.github.io/z-vector-search/&quot;&gt;z-vector-search&lt;/a&gt;&lt;/strong&gt;, a native and open source z/OS engine that allows you to index and query your own data locally. It is also available as a library, so you can embed the same retrieval pipeline directly into your own applications. No cloud dependencies, no data leaving the LPAR, and no more manual flipping through IBM manuals. It’s about building RAG directly where the data lives. It’s also worth noting that much of the code for the project, including a substantial part of the work needed to get llama.cpp embedding support running cleanly on z/OS, was written with the help of &lt;strong&gt;&lt;a href=&quot;https://bob.ibm.com/&quot;&gt;IBM Bob&lt;/a&gt;&lt;/strong&gt;, IBM’s AI Coding Assistant.&lt;/p&gt;

&lt;p&gt;The scenario that motivated all of this is simple: a z/OS system programmer staring at a console flooded with messages like ABENDs, RACF violations, dataset allocation errors, trying to figure out which ones matter, what they mean, and whether the system has seen anything like this before. Today that means flipping between IBM message manuals, internal runbooks, and ticket histories. What if you could just &lt;em&gt;ask&lt;/em&gt;? And what if the answer came from &lt;strong&gt;directly on z/OS&lt;/strong&gt;, not by shipping log data to a cloud LLM, but right there on the LPAR where the data already lives?&lt;/p&gt;

&lt;p&gt;This blog covers how we built &lt;strong&gt;z-vector-search&lt;/strong&gt;, the technical decisions behind it, and how &lt;strong&gt;z-console&lt;/strong&gt;, an operator console enrichment tool, serves as one prototype application built on top of it.&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/index.gif&quot; alt=&quot;tmux.cpp&quot; style=&quot;float:center;&quot; /&gt;
z-vector-search running directly on z/OS
&lt;/p&gt;

&lt;h2 id=&quot;building-a-rag-system-for-zos&quot;&gt;Building a RAG system for z/OS&lt;/h2&gt;

&lt;p&gt;If you’re interested in using the tools and less about learning, go to the &lt;a href=&quot;#getting-started&quot;&gt;Getting Started&lt;/a&gt; section.&lt;/p&gt;

&lt;h3 id=&quot;what-rag-means-on-zos&quot;&gt;What RAG Means on z/OS&lt;/h3&gt;

&lt;p&gt;For a lot of enterprise systems, the missing piece is not generation, it is retrieval. The useful context already exists, but it is scattered across message manuals, procedures, runbooks, ticket histories, and operational logs. A local retrieval engine turns that fragmented knowledge into something you can actually search with natural language, exact identifiers, or a mix of both.&lt;/p&gt;

&lt;p&gt;That is the role of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt;. It provides the retrieval layer for on-prem RAG on z/OS: index data locally, search it locally, and return grounded context locally. You can then use that context directly in a CLI, inside an application, or as input to a later generation step. But if retrieval cannot happen securely on the platform where the data lives, the rest of the RAG pipeline is mostly academic.&lt;/p&gt;

&lt;p&gt;The RAG idea actually came from a &lt;a href=&quot;https://github.com/ggml-org/llama.cpp/discussions/7712&quot;&gt;llama.cpp discussion thread&lt;/a&gt; about adding embedding model support. Reading through it, I realized that all the pieces I needed to build a z/OS RAG system were already on the table, I just had to wire them up.&lt;/p&gt;

&lt;p&gt;But “wiring it up” was only possible because of the stable foundation provided by the &lt;strong&gt;&lt;a href=&quot;https://github.com/zopencommunity/llamacppport&quot;&gt;zopen llamacpp port&lt;/a&gt;&lt;/strong&gt;. That port was a true community effort, driven by a dedicated group of volunteers and university students who worked tirelessly to bring modern AI tools to the mainframe. Their contributions to the core infrastructure and math optimizations are what allowed us to reach this point.&lt;/p&gt;

&lt;h3 id=&quot;whats-an-embedding-anyway&quot;&gt;What’s an embedding, anyway?&lt;/h3&gt;

&lt;p&gt;If you’ve never worked with them, embeddings are the trick that makes “semantic search” possible. An embedding model takes a piece of text and turns it into a list of numbers, a &lt;strong&gt;vector&lt;/strong&gt;, that captures its meaning. The clever part is that two pieces of text with similar meanings produce vectors that are mathematically close to each other in space, even if they share no words in common.&lt;/p&gt;

&lt;p&gt;That means a search for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;dataset allocation failure&quot;&lt;/code&gt; can find a document that says &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;IEC070I&quot;&lt;/code&gt;, because both phrases live near each other in vector space. No keyword matching, no synonyms list, no manual rules. The model has already learned what things &lt;em&gt;mean&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To do search with embeddings, you embed every document once and store the vectors. At query time, you embed the query the same way and find the documents whose vectors are nearest yours.&lt;/p&gt;

&lt;h3 id=&quot;the-model&quot;&gt;The Model&lt;/h3&gt;

&lt;p&gt;The model I chose was &lt;strong&gt;&lt;a href=&quot;https://huggingface.co/nomic-ai/nomic-embed-text-v1.5&quot;&gt;Nomic Embed Text v1.5&lt;/a&gt;&lt;/strong&gt;. Quantized to Q4_K_M, it’s just ~84 MB, small enough to run comfortably on z/OS, and well-regarded for retrieval tasks. It’s an &lt;strong&gt;encoder-only&lt;/strong&gt; model (think BERT-style), which means it’s purpose-built for turning text into vectors rather than generating new text.&lt;/p&gt;

&lt;h3 id=&quot;what-it-took-to-get-working&quot;&gt;What It Took to Get Working&lt;/h3&gt;

&lt;p&gt;llama.cpp’s embedding support is newer than its text generation support, so a few things needed attention to make it behave on z/OS. A substantial part of this implementation work was done with IBM Bob:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Encoder model code path.&lt;/strong&gt; Encoder-only models like Nomic take a different route through llama.cpp than decoder models like LLaMa. They produce one vector per input rather than streaming tokens, which means a different API (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;llama_encode()&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;llama_decode()&lt;/code&gt;) and slightly different batch handling.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Pooling.&lt;/strong&gt; The model produces a vector for every token, but you want a single vector per document. Nomic expects MEAN pooling, averaging the per-token vectors together. Getting this wrong produces embeddings that &lt;em&gt;look&lt;/em&gt; fine but retrieve nonsense.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Document and query prefixes.&lt;/strong&gt; Nomic uses a clever convention where you prepend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search_document:&lt;/code&gt; to text you’re indexing and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search_query:&lt;/code&gt; to text you’re searching for. This subtly nudges the model to put documents and queries in slightly different regions of the embedding space, which measurably improves retrieval quality. A simple trick, but it makes a real difference.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The endianness problem, again!&lt;/strong&gt; Just like with the original llama.cpp port, endianness came back to haunt me. Embedding vectors are arrays of 32-bit floats, and a database built on little-endian platforms needs every float byte-swapped before z/OS (big-endian) can read them. I added automatic endianness detection and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--convert-endian&lt;/code&gt; flag to enable a high-performance hybrid workflow: you can &lt;strong&gt;seed your knowledge base on a fast Linux or macOS box&lt;/strong&gt; (where indexing thousands of documents takes seconds) and then ship the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.db&lt;/code&gt; file over to z/OS for production use. This gives you the best of both worlds: massive throughput for the initial data ingestion and secure, local semantic search where it matters most.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After working through these, I had embeddings producing sensible vectors on z/OS, and that was enough to start building something real.&lt;/p&gt;

&lt;h2 id=&quot;building-the-search-engine&quot;&gt;Building the Search Engine&lt;/h2&gt;

&lt;p&gt;With embeddings now working on z/OS, the next step was obvious: build a persistent vector store so you could index documents once and query them repeatedly.&lt;/p&gt;

&lt;h3 id=&quot;storage-sqlite--sqlite-vec&quot;&gt;Storage: SQLite + sqlite-vec&lt;/h3&gt;

&lt;p&gt;I chose &lt;strong&gt;SQLite&lt;/strong&gt; as the backend, extended with &lt;strong&gt;&lt;a href=&quot;https://github.com/asg017/sqlite-vec&quot;&gt;sqlite-vec&lt;/a&gt;&lt;/strong&gt; for vector similarity search. The combination made it extremely simple: no database server to manage, no network dependencies, just a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.db&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The schema stores each text chunk alongside its embedding and metadata.&lt;/p&gt;

&lt;h3 id=&quot;chunking&quot;&gt;Chunking&lt;/h3&gt;

&lt;p&gt;Large documents can’t be embedded as a single unit: encoder models have a token limit, and long texts lose detail when compressed into one vector. So documents are split into overlapping chunks, 256 tokens each, with 64 tokens of overlap between adjacent chunks. The overlap ensures context at chunk boundaries isn’t lost, and each chunk is independently embedded and stored.&lt;/p&gt;

&lt;h3 id=&quot;the-tools&quot;&gt;The Tools&lt;/h3&gt;

&lt;p&gt;To make this process seamless, I created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-setup&lt;/code&gt;, a simple initialization tool. It handles the heavy lifting: unpacking the pre-built messages database, performing the necessary big-endian conversion for z/OS, and downloading the optimized Nomic embedding model.&lt;/p&gt;

&lt;p&gt;The project is composed of a suite of command-line tools:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Tool&lt;/th&gt;
      &lt;th&gt;Purpose&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-index&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Index documents into the persistent vector store&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-query&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Search the store with natural language queries&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;One-shot mode: index and query without persistence&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;A typical workflow:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Index your documents&lt;/span&gt;
z-index &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.txt

&lt;span class=&quot;c&quot;&gt;# Search with natural language&lt;/span&gt;
z-query &lt;span class=&quot;s2&quot;&gt;&quot;how do I recover from an IEC070I error&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The query returns the most semantically relevant chunks, ranked by similarity. If your runbook says “dataset allocation failure” and you search for “IEC070I error,” it still finds the right answer.&lt;/p&gt;

&lt;p&gt;All tools support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--json&lt;/code&gt; output for scripting, so you can pipe results into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;z-query &lt;span class=&quot;nt&quot;&gt;--json&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dataset allocation failure&quot;&lt;/span&gt; | jq &lt;span class=&quot;s1&quot;&gt;&apos;.results[0].snippet&apos;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;hybrid-search&quot;&gt;Hybrid Search&lt;/h2&gt;

&lt;p&gt;Pure semantic search is powerful, but sometimes you know exactly what you’re looking for. If an operator sees &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH408I&lt;/code&gt; and wants to look it up, they don’t need semantic similarity, they need an exact match.&lt;/p&gt;

&lt;p&gt;So z-query automatically classifies each query:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH408I&lt;/code&gt; → keyword search (exact message ID via SQL LIKE)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DFH*&lt;/code&gt; → keyword search (wildcard)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MSGID:IEF JOB:PAYROLL&lt;/code&gt; → keyword search (structured prefix)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;why is my CICS transaction failing&lt;/code&gt; → semantic search (natural language)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH408I unauthorized access&lt;/code&gt; → hybrid (both, merged)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When both modes run, results are merged using &lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt;, a technique for combining ranked lists without needing to normalize scores across different methods. The formula is simple:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;score = Σ 1/(k + rank)    where k = 60
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-ibm-zos-messages-knowledge-base&quot;&gt;The IBM z/OS Messages Knowledge Base&lt;/h2&gt;

&lt;p&gt;A semantic search engine is only as good as its data. To make the tool immediately useful for z/OS operators, I built a pre-packaged knowledge base of &lt;strong&gt;24,565 IBM z/OS messages&lt;/strong&gt;, covering MVS and system abend/wait codes. Each entry includes the message ID, explanation, system action, and operator response.&lt;/p&gt;

&lt;p&gt;The knowledge base ships as a ready-to-use SQLite database, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-query&lt;/code&gt; can answer questions about IBM messages out of the box:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;z-query &lt;span class=&quot;s2&quot;&gt;&quot;what does abend S0C4 mean&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This returns the relevant system code documentation explaining that S0C4 is a protection exception, typically caused by a program accessing storage it doesn’t own.&lt;/p&gt;

&lt;h2 id=&quot;z-console-one-application-on-top-of-the-engine&quot;&gt;z-console: One Application on Top of the Engine&lt;/h2&gt;

&lt;p&gt;The IBM messages knowledge base makes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-query&lt;/code&gt; useful immediately, but it is only one way to use the engine. To show what a full application built on top of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt; can look like, I also built &lt;strong&gt;z-console&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Built on top of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt;, &lt;strong&gt;z-console&lt;/strong&gt; brings the same retrieval pipeline directly to the operator console. It is the answer to the question from the intro: what if a z/OS operator could just &lt;em&gt;ask&lt;/em&gt; about a console message?&lt;/p&gt;

&lt;p&gt;It comes pre-packaged with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt; suite. It builds directly on the core &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt; engine, using it as a library to perform real-time semantic lookups.&lt;/p&gt;

&lt;p&gt;The z/OS operator console is the nerve center of a mainframe system. Messages stream in constantly: job completions, security events, storage allocations, errors, abends. Experienced operators know what to look for, but the volume is overwhelming, and critical messages can be buried in the noise.&lt;/p&gt;

&lt;p&gt;z-console reads your console messages and enriches each one with relevant context from both IBM documentation and your system’s own operational history, all by running z-vector-search under the hood.&lt;/p&gt;

&lt;h3 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Read&lt;/strong&gt;, pulls messages from the z/OS SYSLOG via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pcon&lt;/code&gt; (an IBM ZOAU utility that reads the system log)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt;, picks out high-value messages: abends (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEF*&lt;/code&gt;), data errors (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEC*&lt;/code&gt;), RACF violations (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH*&lt;/code&gt;), CICS (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DFH*&lt;/code&gt;), DB2 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DSN*&lt;/code&gt;), MQ (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSQ*&lt;/code&gt;), and anything with action/error severity&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Look up&lt;/strong&gt;, for each interesting message, runs a two-phase search:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Keyword&lt;/strong&gt; against the IBM messages knowledge base, what does this message ID mean?&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Semantic&lt;/strong&gt; against your operational history, have we seen something like this before?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Display&lt;/strong&gt;, presents everything with color-coded severity and ranked context&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;input-modes&quot;&gt;Input Modes&lt;/h3&gt;

&lt;p&gt;z-console has three ways to feed it messages:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Single message, the simplest starting point&lt;/span&gt;
z-console &lt;span class=&quot;s2&quot;&gt;&quot;ICH408I USER(BATCH1) GROUP(PROD) LOGON/JOB INITIATION - ACCESS REVOKED&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Live console via pcon&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;                    &lt;span class=&quot;c&quot;&gt;# last hour&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; 30                 &lt;span class=&quot;c&quot;&gt;# last 30 minutes&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--since&lt;/span&gt; 2026-04-06T10:00     &lt;span class=&quot;c&quot;&gt;# since a specific timestamp&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Pipe from stdin&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;syslog.txt | z-console
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-output&quot;&gt;Example Output&lt;/h3&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-console --pcon -l&lt;/code&gt; on a system with an access violation might produce:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Parsed 847 messages, 23 interesting, 14 unique IDs to look up.

━━━ ICH408I (severity: E) ━━━
  ICH408I USER(BATCH1) GROUP(PROD) NAME(BATCH JOB)
    LOGON/JOB INITIATION - ACCESS REVOKED

  IBM Documentation (keyword match):
     ICH408I - A RACF-defined user has been revoked. The user&apos;s access
     authority has been removed, typically because consecutive incorrect
     password attempts exceeded the SETROPTS PASSWORD limit.
     System Action: The logon or job is rejected.
     Operator Response: Contact the security administrator to reinstate
     access via ALTUSER userid RESUME.
     (distance: 0.12)

  Operational History (semantic match):
     [2026-04-03 14:22] ICH408I,ICH409I, BATCH1 revoked on SYS1,
     resolved by security team reset. Related: RACF password policy
     change ticket INC-4421.
     (distance: 0.31)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In a single glance, the operator knows what the message means &lt;em&gt;and&lt;/em&gt; that it’s happened before, with a pointer to how it was resolved last time. That’s the whole pitch for RAG on the console.&lt;/p&gt;

&lt;h3 id=&quot;building-operational-history&quot;&gt;Building Operational History&lt;/h3&gt;

&lt;p&gt;To power the “have we seen this before?” lookups, there’s a companion tool called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-ingest-console&lt;/code&gt;. It runs as a background daemon via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-console-daemon.sh&lt;/code&gt; (every 5 minutes by default) and continuously indexes console messages into the vector store:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Start the daemon in the background&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;nohup&lt;/span&gt; ./z-console-daemon.sh &amp;amp;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Messages are grouped into 5-minute time windows and stored with structured metadata, message IDs, highest severity, jobname, system name, timestamps. The longer it runs, the more historical context z-console can draw on.&lt;/p&gt;

&lt;h2 id=&quot;the-full-picture-enabling-rag-directly-on-zos&quot;&gt;The Full Picture: Enabling RAG Directly on z/OS&lt;/h2&gt;

&lt;p&gt;By bringing together embeddings, a persistent vector store, and a hybrid search engine, &lt;strong&gt;z-vector-search&lt;/strong&gt; provides the retrieval core for a complete &lt;strong&gt;Retrieval-Augmented Generation (RAG) workflow that works directly on z/OS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the air-gapped environments common in finance and healthcare, it means you can build assistants and retrieval-driven tools that understand your specific system configuration and historical data without a single byte leaving your secure LPAR.&lt;/p&gt;

&lt;p&gt;Here is the general pipeline that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt; implements, whether the caller is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-query&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-console&lt;/code&gt;, or your own embedded application:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Documents / Logs / Runbooks / Message KB
        ↓
   Tokenize (llama.cpp)
        ↓
   Chunk (256 tokens, 64 overlap)
        ↓
   Embed (Nomic Embed v1.5)
        ↓
   L2 Normalize
        ↓
   Store (SQLite + sqlite-vec)
        ↓
   Query → Classify → Keyword / Semantic / Hybrid
        ↓
   Reciprocal Rank Fusion
        ↓
   Top-K Results with Context
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Everything runs locally on z/OS. No external API calls, no cloud dependencies, no data leaving the LPAR. The generation step, if you want one, can sit on top later. The important part is that the retrieval foundation already runs where the data lives.&lt;/p&gt;

&lt;p&gt;The entire stack is pure C++17 with SQLite, sqlite-vec, and llama.cpp. No Python runtime, no Java, no external dependencies beyond what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; provides.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; you’ll need the &lt;a href=&quot;https://zopen.community&quot;&gt;zopen package manager&lt;/a&gt; set up on your z/OS system. If you haven’t used zopen before, the &lt;a href=&quot;https://zopen.community/#/Guides/QuickStart&quot;&gt;QuickStart Guide&lt;/a&gt; takes about five minutes.&lt;/p&gt;

&lt;p&gt;The simplest way to get going is straight from zopen:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;z-vector-search
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, regardless of how you installed:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 3. Run setup, downloads the model and unpacks the IBM messages DB&lt;/span&gt;
z-setup

&lt;span class=&quot;c&quot;&gt;# 4. Query the IBM messages knowledge base&lt;/span&gt;
z-query &lt;span class=&quot;s2&quot;&gt;&quot;what does abend S0C4 mean&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 5. Index your own runbooks or operational docs&lt;/span&gt;
z-index /path/to/runbooks/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.txt

&lt;span class=&quot;c&quot;&gt;# 6. Search them semantically&lt;/span&gt;
z-query &lt;span class=&quot;s2&quot;&gt;&quot;how do I recover from an IEC070I error&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 7. Optional: look up a single console message&lt;/span&gt;
z-console &lt;span class=&quot;s2&quot;&gt;&quot;ICH408I USER(BATCH1) GROUP(PROD) LOGON/JOB INITIATION - ACCESS REVOKED&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 8. Optional: read the last hour of live console&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The source code is available on &lt;a href=&quot;https://github.com/IgorTodorovskiIBM/z-vector-search&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;What started as “can we get embeddings working on z/OS?” turned into a practical retrieval layer for RAG on the mainframe. Embeddings gave us semantic understanding. A vector store made it persistent. Hybrid search made it practical for people who think in message IDs, commands, and procedures, not just natural language. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-console&lt;/code&gt; tied it into one concrete operator workflow, but the bigger point is that secure, local retrieval on z/OS is now a real building block you can use elsewhere too.&lt;/p&gt;

&lt;p&gt;Thank you to Bill O’Farrell, Chad McIntyre, James Tang, Haritha D, and Sachin T for their support and feedback!&lt;/p&gt;
</description>
        <pubDate>Mon, 20 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2026/04/20/z-vector-search/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2026/04/20/z-vector-search/</guid>
        
        <category>z/OS</category>
        
        <category>AI</category>
        
        <category>llama.cpp</category>
        
        <category>embeddings</category>
        
        <category>vector-search</category>
        
        <category>RAG</category>
        
        <category>zopen</category>
        
        <category>SIMD</category>
        
        
      </item>
    
      <item>
        <title>From Porting to RAG: Building a Vector Search Engine for z/OS</title>
        <description>&lt;p&gt;In a &lt;a href=&quot;https://igortodorovskiibm.github.io/blog/2023/08/22/llama.cpp/&quot;&gt;previous blog post&lt;/a&gt;, we proved that running a 7B parameter LLM on z/OS was possible. It was a milestone, but a model in isolation is just a curiosity. The real question is: &lt;strong&gt;how do we make it useful?&lt;/strong&gt; For a z/OS system programmer, utility isn’t found in generating poetry; it’s found in navigating the “data deluge” of the operator console.&lt;/p&gt;

&lt;p&gt;Today, we’re moving from simple text generation to &lt;strong&gt;semantic retrieval&lt;/strong&gt;. This post introduces &lt;strong&gt;&lt;a href=&quot;https://github.com/IgorTodorovskiIBM/z-vector-search&quot;&gt;z-vector-search&lt;/a&gt;&lt;/strong&gt;, a native z/OS engine that allows you to index your own documentation and logs locally. No cloud dependencies, no data leaving the LPAR, and no more manual flipping through IBM manuals. We’re building Retrieval-Augmented Generation (RAG) directly where the data lives.&lt;/p&gt;

&lt;p&gt;The scenario that motivated all of this is simple: a z/OS system programmer staring at a console flooded with messages — ABENDs, RACF violations, dataset allocation errors — trying to figure out which ones matter, what they mean, and whether the system has seen anything like this before. Today that means flipping between IBM message manuals, internal runbooks, and ticket histories. What if you could just &lt;em&gt;ask&lt;/em&gt;? And critically, what if the answer came from &lt;strong&gt;directly on z/OS&lt;/strong&gt;, not by shipping log data to a cloud LLM, but right there on the LPAR where the data already lives?&lt;/p&gt;

&lt;p&gt;This post covers how we built z-vector-search, the technical decisions behind it, and how &lt;strong&gt;z-console&lt;/strong&gt; — an operator console enrichment tool — serves as a prototype real-world application on top of it. Along the way, we’ll also look at the SIMD vectorization work that made the whole thing fast enough to actually use on z/OS.&lt;/p&gt;

&lt;h2 id=&quot;getting-embeddings-working-on-zos&quot;&gt;Getting Embeddings Working on z/OS&lt;/h2&gt;

&lt;p&gt;The idea actually came from a &lt;a href=&quot;https://github.com/ggml-org/llama.cpp/discussions/7712&quot;&gt;llama.cpp discussion thread&lt;/a&gt; about adding embedding model support. Reading through it, I realized that all the pieces I needed to build a z/OS RAG system were already on the table, I just had to wire them up.&lt;/p&gt;

&lt;p&gt;But “wiring it up” was only possible because of the stable foundation provided by the &lt;strong&gt;&lt;a href=&quot;https://github.com/zopencommunity/llamacppport&quot;&gt;zopen llamacpp port&lt;/a&gt;&lt;/strong&gt;. That port was a true community effort, driven by a dedicated group of volunteers and university students who worked tirelessly to bring modern AI tools to the mainframe. Their contributions to the core infrastructure and math optimizations are what allowed us to reach this point.&lt;/p&gt;

&lt;h3 id=&quot;whats-an-embedding-anyway&quot;&gt;What’s an embedding, anyway?&lt;/h3&gt;

&lt;p&gt;If you’ve never worked with them, embeddings are the trick that makes “semantic search” possible. An embedding model takes a piece of text and turns it into a list of numbers, a &lt;strong&gt;vector&lt;/strong&gt;, that captures its meaning. The clever part is that two pieces of text with similar meanings produce vectors that are mathematically close to each other in space, even if they share no words in common.&lt;/p&gt;

&lt;p&gt;That means a search for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;dataset allocation failure&quot;&lt;/code&gt; can find a document that says &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;IEC070I&quot;&lt;/code&gt;, because both phrases live near each other in vector space. No keyword matching, no synonyms list, no manual rules. The model has already learned what things &lt;em&gt;mean&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To do search with embeddings, you embed every document once and store the vectors. At query time, you embed the query the same way and find the documents whose vectors are nearest yours. That’s the whole game.&lt;/p&gt;

&lt;h3 id=&quot;the-model&quot;&gt;The Model&lt;/h3&gt;

&lt;p&gt;The model I chose was &lt;strong&gt;Nomic Embed Text v1.5&lt;/strong&gt;. Quantized to Q4_K_M, it’s just ~84 MB, small enough to run comfortably on z/OS, and well-regarded for retrieval tasks. It’s an &lt;strong&gt;encoder-only&lt;/strong&gt; model (think BERT-style), which means it’s purpose-built for turning text into vectors rather than generating new text.&lt;/p&gt;

&lt;h3 id=&quot;what-it-took-to-get-working&quot;&gt;What It Took to Get Working&lt;/h3&gt;

&lt;p&gt;llama.cpp’s embedding support is newer than its text generation support, so a few things needed attention to make it behave on z/OS:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Encoder model code path.&lt;/strong&gt; Encoder-only models like Nomic take a different route through llama.cpp than decoder models like LLaMa. They produce one vector per input rather than streaming tokens, which means a different API (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;llama_encode()&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;llama_decode()&lt;/code&gt;) and slightly different batch handling.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Pooling.&lt;/strong&gt; The model produces a vector for every token, but you want a single vector per document. Nomic expects MEAN pooling, averaging the per-token vectors together. Getting this wrong produces embeddings that &lt;em&gt;look&lt;/em&gt; fine but retrieve nonsense.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Document and query prefixes.&lt;/strong&gt; Nomic uses a clever convention where you prepend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search_document:&lt;/code&gt; to text you’re indexing and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search_query:&lt;/code&gt; to text you’re searching for. This subtly nudges the model to put documents and queries in slightly different regions of the embedding space, which measurably improves retrieval quality. A simple trick, but it makes a real difference.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The endianness problem, again!&lt;/strong&gt; Just like with the original llama.cpp port, endianness came back to haunt me. Embedding vectors are arrays of 32-bit floats, and a database built on x86 (little-endian) needs every float byte-swapped before z/OS (big-endian) can read them. I added automatic endianness detection and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--convert-endian&lt;/code&gt; flag to enable a high-performance hybrid workflow: you can &lt;strong&gt;seed your knowledge base on a fast Linux or macOS box&lt;/strong&gt; (where indexing thousands of documents takes seconds) and then ship the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.db&lt;/code&gt; file over to z/OS for production use. This gives you the best of both worlds: massive throughput for the initial data ingestion and secure, local semantic search where it matters most.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After working through these, I had embeddings producing sensible vectors on z/OS, and that was enough to start building something real.&lt;/p&gt;

&lt;h2 id=&quot;building-the-search-engine&quot;&gt;Building the Search Engine&lt;/h2&gt;

&lt;p&gt;With embeddings now working on z/OS, the next step was obvious: build a persistent vector store so you could index documents once and query them repeatedly.&lt;/p&gt;

&lt;h3 id=&quot;storage-sqlite--sqlite-vec&quot;&gt;Storage: SQLite + sqlite-vec&lt;/h3&gt;

&lt;p&gt;I chose &lt;strong&gt;SQLite&lt;/strong&gt; as the backend, extended with &lt;strong&gt;sqlite-vec&lt;/strong&gt; for vector similarity search. The combination is simple and elegant: no database server to manage, no network dependencies, just a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.db&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The schema stores each text chunk alongside its embedding and metadata.&lt;/p&gt;

&lt;h3 id=&quot;chunking&quot;&gt;Chunking&lt;/h3&gt;

&lt;p&gt;Large documents can’t be embedded as a single unit: encoder models have a token limit, and long texts lose detail when compressed into one vector. So documents are split into overlapping chunks, 256 tokens each, with 64 tokens of overlap between adjacent chunks. The overlap ensures context at chunk boundaries isn’t lost, and each chunk is independently embedded and stored.&lt;/p&gt;

&lt;h3 id=&quot;the-tools&quot;&gt;The Tools&lt;/h3&gt;

&lt;p&gt;The project is composed of a suite of command-line tools:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Tool&lt;/th&gt;
      &lt;th&gt;Purpose&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-index&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Index documents into the persistent vector store&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-query&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Search the store with natural language queries&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-vector-search&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;One-shot mode: index and query without persistence&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;A typical workflow:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Index your runbooks&lt;/span&gt;
z-index &lt;span class=&quot;nt&quot;&gt;--store&lt;/span&gt; ~/my-store.db /path/to/runbooks/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.txt

&lt;span class=&quot;c&quot;&gt;# Search with natural language&lt;/span&gt;
z-query &lt;span class=&quot;nt&quot;&gt;--store&lt;/span&gt; ~/my-store.db &lt;span class=&quot;s2&quot;&gt;&quot;how do I recover from an IEC070I error&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The query returns the most semantically relevant chunks, ranked by similarity. No keyword matching needed, if your runbook says “dataset allocation failure” and you search for “IEC070I error,” it still finds the right answer.&lt;/p&gt;

&lt;p&gt;All tools support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--json&lt;/code&gt; output for scripting, so you can pipe results into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;z-query &lt;span class=&quot;nt&quot;&gt;--json&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dataset allocation failure&quot;&lt;/span&gt; | jq &lt;span class=&quot;s1&quot;&gt;&apos;.results[0].snippet&apos;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;hybrid-search&quot;&gt;Hybrid Search&lt;/h2&gt;

&lt;p&gt;Pure semantic search is powerful, but sometimes you know exactly what you’re looking for. If an operator sees &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH408I&lt;/code&gt; and wants to look it up, they don’t need semantic similarity, they need an exact match.&lt;/p&gt;

&lt;p&gt;So z-query automatically classifies each query:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH408I&lt;/code&gt; → keyword search (exact message ID via SQL LIKE)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DFH*&lt;/code&gt; → keyword search (wildcard)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MSGID:IEF JOB:PAYROLL&lt;/code&gt; → keyword search (structured prefix)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;why is my CICS transaction failing&lt;/code&gt; → semantic search (natural language)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH408I unauthorized access&lt;/code&gt; → hybrid (both, merged)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When both modes run, results are merged using &lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt;, a technique for combining ranked lists without needing to normalize scores across different methods. The formula is simple:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;score = Σ 1/(k + rank)    where k = 60
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-ibm-zos-messages-knowledge-base&quot;&gt;The IBM z/OS Messages Knowledge Base&lt;/h2&gt;

&lt;p&gt;A semantic search engine is only as good as its data. To make the tool immediately useful for z/OS operators, I built a pre-packaged knowledge base of &lt;strong&gt;24,565 IBM z/OS messages&lt;/strong&gt;, covering MVS and system abend/wait codes. Each entry includes the message ID, explanation, system action, and operator response.&lt;/p&gt;

&lt;p&gt;The knowledge base ships as a ready-to-use SQLite database, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-query&lt;/code&gt; can answer questions about IBM messages out of the box:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;z-query &lt;span class=&quot;s2&quot;&gt;&quot;what does abend S0C4 mean&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This returns the relevant system code documentation explaining that S0C4 is a protection exception, typically caused by a program accessing storage it doesn’t own.&lt;/p&gt;

&lt;h2 id=&quot;z-console-rag-for-the-operator-console&quot;&gt;z-console: RAG for the Operator Console&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;z-console&lt;/strong&gt; is a prototype implementation of a real-world scenario built on top of z-vector-search. It’s the answer to the question from the intro: what if a z/OS operator could just &lt;em&gt;ask&lt;/em&gt; about a console message?&lt;/p&gt;

&lt;p&gt;The z/OS operator console is the nerve center of a mainframe system. Messages stream in constantly, job completions, security events, storage allocations, errors, abends. Experienced operators know what to look for, but the volume is overwhelming, and critical messages can be buried in noise.&lt;/p&gt;

&lt;p&gt;z-console reads your console messages and enriches each one with relevant context from both IBM documentation and your system’s own operational history, all by running z-vector-search under the hood.&lt;/p&gt;

&lt;h3 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Read&lt;/strong&gt;, pulls messages from the z/OS SYSLOG via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pcon&lt;/code&gt; (an IBM ZOAU utility that reads the system log)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt;, picks out high-value messages: abends (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEF*&lt;/code&gt;), data errors (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEC*&lt;/code&gt;), RACF violations (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH*&lt;/code&gt;), CICS (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DFH*&lt;/code&gt;), DB2 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DSN*&lt;/code&gt;), MQ (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSQ*&lt;/code&gt;), and anything with action/error severity&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Look up&lt;/strong&gt;, for each interesting message, runs a two-phase search:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Keyword&lt;/strong&gt; against the IBM messages knowledge base, what does this message ID mean?&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Semantic&lt;/strong&gt; against your operational history, have we seen something like this before?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Display&lt;/strong&gt;, presents everything with color-coded severity and ranked context&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;input-modes&quot;&gt;Input Modes&lt;/h3&gt;

&lt;p&gt;z-console has three ways to feed it messages:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Single message, the simplest starting point&lt;/span&gt;
z-console &lt;span class=&quot;s2&quot;&gt;&quot;ICH408I USER(BATCH1) GROUP(PROD) LOGON/JOB INITIATION - ACCESS REVOKED&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Live console via pcon&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;                    &lt;span class=&quot;c&quot;&gt;# last hour&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; 30                 &lt;span class=&quot;c&quot;&gt;# last 30 minutes&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--since&lt;/span&gt; 2026-04-06T10:00     &lt;span class=&quot;c&quot;&gt;# since a specific timestamp&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Pipe from stdin&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;syslog.txt | z-console
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;example-output&quot;&gt;Example Output&lt;/h3&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-console --pcon -l&lt;/code&gt; on a system with an access violation might produce:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Parsed 847 messages, 23 interesting, 14 unique IDs to look up.

━━━ ICH408I (severity: E) ━━━
  ICH408I USER(BATCH1) GROUP(PROD) NAME(BATCH JOB)
    LOGON/JOB INITIATION - ACCESS REVOKED

  IBM Documentation (keyword match):
     ICH408I - A RACF-defined user has been revoked. The user&apos;s access
     authority has been removed, typically because consecutive incorrect
     password attempts exceeded the SETROPTS PASSWORD limit.
     System Action: The logon or job is rejected.
     Operator Response: Contact the security administrator to reinstate
     access via ALTUSER userid RESUME.
     (distance: 0.12)

  Operational History (semantic match):
     [2026-04-03 14:22] ICH408I,ICH409I, BATCH1 revoked on SYS1,
     resolved by security team reset. Related: RACF password policy
     change ticket INC-4421.
     (distance: 0.31)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In a single glance, the operator knows what the message means &lt;em&gt;and&lt;/em&gt; that it’s happened before, with a pointer to how it was resolved last time. That’s the whole pitch for RAG on the console.&lt;/p&gt;

&lt;h3 id=&quot;summary-mode&quot;&gt;Summary Mode&lt;/h3&gt;

&lt;p&gt;Sometimes you don’t need full RAG enrichment, just a quick health check. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--summary&lt;/code&gt; groups messages by severity and category without loading the embedding model at all:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;z-console &lt;span class=&quot;nt&quot;&gt;--summary&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;=== Console Summary (last hour) ===
Total messages: 847 | Interesting: 23

  CRITICAL/ERROR (3):
    ICH408I  ×2  USER(BATCH1) LOGON/JOB INITIATION - ACCESS REVOKED
    IEC030I  ×1  I/O ERROR, DATASET SYS1.LINKLIB

  WARNING (5):
    IEA404W  ×3  REAL STORAGE SHORTAGE
    CSV028W  ×2  MODULE NOT FOUND IN LINKLIST

  INFORMATIONAL (15):
    DFH1501I ×8  CICS TRANSACTION COMPLETED
    DSN9022I ×7  DB2 COMMAND COMPLETED
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Fast enough to run frequently, and gives operators an at-a-glance view of system health.&lt;/p&gt;

&lt;h3 id=&quot;building-operational-history&quot;&gt;Building Operational History&lt;/h3&gt;

&lt;p&gt;To power the “have we seen this before?” lookups, there’s a companion tool called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-ingest-console&lt;/code&gt;. It runs as a background daemon via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-console-daemon.sh&lt;/code&gt; (every 5 minutes by default) and continuously indexes console messages into the vector store:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Start the daemon in the background&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;nohup&lt;/span&gt; ./z-console-daemon.sh &amp;amp;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Messages are grouped into 5-minute time windows and stored with structured metadata, message IDs, highest severity, jobname, system name, timestamps. The longer it runs, the more historical context z-console can draw on.&lt;/p&gt;

&lt;h2 id=&quot;measuring-performance&quot;&gt;Measuring Performance&lt;/h2&gt;

&lt;p&gt;How fast is all of this? Rather than hardcoding numbers into this post, where they’d go stale the moment you run on different hardware, both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-query&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z-console&lt;/code&gt; support a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--metrics&lt;/code&gt; flag that outputs timing data as JSON on stderr.&lt;/p&gt;

&lt;p&gt;For a query:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;z-query &lt;span class=&quot;nt&quot;&gt;--metrics&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;what does abend S0C4 mean&quot;&lt;/span&gt; 2&amp;gt;metrics.json
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;semantic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;model_load_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2341.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;embed_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;287.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;search_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;42.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2812.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;results&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;store_chunks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;34102&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For z-console, the metrics break down timing across all enriched messages, with per-message averages:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;z-console &lt;span class=&quot;nt&quot;&gt;--metrics&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; 2&amp;gt;metrics.json
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_parsed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;847&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;interesting&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;skipped&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;824&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;unique_ids&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cache_hits&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;enriched&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;model_load_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2341.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_enrich_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;4892.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_embed_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3156.7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_search_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1204.8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;avg_enrich_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;444.7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;avg_embed_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;286.9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;avg_search_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;109.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key insight from the metrics: model load is a one-time cost of a few seconds, and after that each message enrichment takes well under half a second. Keyword-only modes (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--summary&lt;/code&gt;, pure msgid lookups) skip the model entirely and return in milliseconds. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--metrics&lt;/code&gt; output lets you measure exactly what matters on your LPAR, under your workload.&lt;/p&gt;

&lt;h2 id=&quot;the-full-picture-enabling-rag-directly-on-zos&quot;&gt;The Full Picture: Enabling RAG Directly on z/OS&lt;/h2&gt;

&lt;p&gt;By bringing together embeddings, a persistent vector store, and a hybrid search engine, we’ve enabled a complete &lt;strong&gt;Retrieval-Augmented Generation (RAG) system that works directly on z/OS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the air-gapped environments common in finance and healthcare, this isn’t just a nice-to-have—it’s a hard requirement. It means you can build intelligent assistants that understand your specific system configuration and historical data without a single byte leaving your secure LPAR.&lt;/p&gt;

&lt;p&gt;Here’s the full pipeline that runs every time z-console enriches a message:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Console Messages / Documents
        ↓
   Tokenize (llama.cpp)
        ↓
   Chunk (256 tokens, 64 overlap)
        ↓
   Embed (Nomic Embed v1.5)
        ↓
   L2 Normalize
        ↓
   Store (SQLite + sqlite-vec)
        ↓
   Query → Classify → Keyword / Semantic / Hybrid
        ↓
   Reciprocal Rank Fusion
        ↓
   Top-K Results with Context
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Everything runs locally on z/OS. No external API calls, no cloud dependencies, no data leaving the LPAR. For the air-gapped environments common in finance and healthcare, that’s not a nice-to-have, it’s a hard requirement.&lt;/p&gt;

&lt;p&gt;The entire stack is pure C++17 with SQLite, sqlite-vec, and llama.cpp. No Python runtime, no Java, no external dependencies beyond what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; provides.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; you’ll need the &lt;a href=&quot;https://zopen.community&quot;&gt;zopen package manager&lt;/a&gt; set up on your z/OS system. If you haven’t used zopen before, the &lt;a href=&quot;https://zopen.community/#/Guides/QuickStart&quot;&gt;QuickStart Guide&lt;/a&gt; takes about five minutes.&lt;/p&gt;

&lt;p&gt;The simplest way to get going is straight from zopen:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;z-vector-search
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, regardless of how you installed:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 3. Run setup, downloads the model and unpacks the IBM messages DB&lt;/span&gt;
z-setup

&lt;span class=&quot;c&quot;&gt;# 4. Query the IBM messages knowledge base&lt;/span&gt;
z-query &lt;span class=&quot;s2&quot;&gt;&quot;what does abend S0C4 mean&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 5. Look up a single console message&lt;/span&gt;
z-console &lt;span class=&quot;s2&quot;&gt;&quot;ICH408I USER(BATCH1) GROUP(PROD) LOGON/JOB INITIATION - ACCESS REVOKED&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 6. Or read the last hour of live console&lt;/span&gt;
z-console &lt;span class=&quot;nt&quot;&gt;--pcon&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The source code is available on &lt;a href=&quot;https://github.com/IgorTodorovskiIBM/z-vector-search&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;What started as “can we get embeddings working on z/OS?” turned into a full RAG-powered operational assistant. Each step revealed the next problem worth solving. Embeddings gave us semantic understanding. A vector store made it persistent. Hybrid search made it practical for operators who think in message IDs, not natural language. z-console tied it all together. And along the way, a round of SIMD vectorization made the whole thing fast enough to actually use.&lt;/p&gt;

&lt;p&gt;The mainframe has always been about running critical workloads reliably. Now it can understand them too.&lt;/p&gt;
</description>
        <pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2026/04/06/z-vector-search/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2026/04/06/z-vector-search/</guid>
        
        <category>z/OS</category>
        
        <category>AI</category>
        
        <category>llama.cpp</category>
        
        <category>embeddings</category>
        
        <category>vector-search</category>
        
        <category>RAG</category>
        
        <category>zopen</category>
        
        <category>SIMD</category>
        
        
      </item>
    
      <item>
        <title>I’ve Got a Crush on AI: Automating your z/OS Workflows</title>
        <description>&lt;p&gt;As a developer in the &lt;a href=&quot;https://zopen.community/&quot;&gt;zopen community&lt;/a&gt;, I’m always looking for ways to streamline my workflow on z/OS. With the rise of agentic AI, I started wondering if I could “agentify” my z/OS workflow.&lt;/p&gt;

&lt;p&gt;Imagine being able to use natural language to automate tasks with familiar tools. For example, what if I could simply ask: &lt;strong&gt;“Show me all of the installed zopen packages that need updating”&lt;/strong&gt; or &lt;strong&gt;“Install everything I need for web development”&lt;/strong&gt;, and have the AI use its knowledge and the zopen package manager to carry out the task?&lt;/p&gt;

&lt;p&gt;Thanks to Large Language Models (LLMs) and the Model Context Protocol (MCP), this is now possible &lt;strong&gt;directly&lt;/strong&gt; on z/OS!&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;

  &lt;a href=&quot;/blog/img/in-post/crush_zopen.gif&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
    &lt;img src=&quot;/blog/img/in-post/crush_zopen.gif&quot; alt=&quot;Crush&quot; title=&quot;Crush Image&quot; style=&quot;display: block; margin: 0 auto;&quot; /&gt;
    Agentic AI running directly on z/OS!
  &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;To make this work, we combine several key technologies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A custom &lt;a href=&quot;https://github.com/IgorTodorovskiIBM/zopen-mcp-server&quot;&gt;zopen MCP Server&lt;/a&gt; that translates AI requests into real zopen commands.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ollama/ollama&quot;&gt;Ollama&lt;/a&gt; or &lt;a href=&quot;https://github.com/ggml-org/llama.cpp&quot;&gt;LLama.cpp&lt;/a&gt;, running an open-source language model directly on your workstation or on z/OS.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/charmbracelet/crush&quot;&gt;Crush&lt;/a&gt;, an open-source terminal-native AI agent that ties everything together.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-is-mcp&quot;&gt;What is MCP&lt;/h3&gt;

&lt;p&gt;Before we start, let’s explain MCP. The &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; is an open standard designed to let AI models safely and reliably interact with external tools (like git, grep and in our case zopen) and data. The way I think of it is as a universal adapter. It defines a standard language that any AI agent can use to “talk” to any tool that also speaks that language.&lt;/p&gt;

&lt;p&gt;The design of MCP is heavily inspired by the success of the &lt;strong&gt;Language Server Protocol (LSP)&lt;/strong&gt;, which I covered in another &lt;a href=&quot;https://igortodorovskiibm.github.io/blog/2024/04/18/vim-autocomplete/&quot;&gt;blog&lt;/a&gt;. Just as LSP created a standard for code editors to communicate with language analysis tools, MCP creates a standard for AI agents to communicate with any tool or data source. This open-standard approach encourages a rich ecosystem where any tool provider can make their service AI-ready, and any AI agent, like Crush, can consume it.&lt;/p&gt;

&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;

&lt;p&gt;Before we begin, you’ll need the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Go 1.23 or later. For z/OS, you can get Go from the &lt;a href=&quot;https://www.ibm.com/products/open-enterprise-sdk-go-zos&quot;&gt;IBM Open Enterprise SDK for Go product page&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;An environment with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; installed (either locally or on a remote z/OS system). Use these &lt;a href=&quot;https://zopen.community/#/Guides/QuickStart&quot;&gt;instructions&lt;/a&gt; if you don’t have zopen installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;step-1-choose-your-language-model&quot;&gt;Step 1: Choose Your Language Model&lt;/h3&gt;

&lt;p&gt;The core of our agent is the Large Language Model. You can leverage any LLM, but given that the zopen community is all about open-source I am going to leverage an open-source model. Whether you run it remotely or on z/OS is up to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A (Recommended): Run the Model remotely (in my case my Workstation) with Ollama&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the best performance and responsiveness, running the LLM on a workstation (as opposed to directly on z/OS) is the ideal choice as long as you have a GPU or Mac M series. Choosing the right model is important and will impact how well the automation workflow behaves.&lt;/p&gt;

&lt;p&gt;For simplicity, I am going to install the LLM on my Mac using Ollama.&lt;/p&gt;

&lt;p&gt;If you use Homebrew, you can install it via the command line.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;ollama
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m going to use Qwen3, because it’s a fairly lightweight model (8b parameter model) and it does well at most coding tasks. If you have enough VRAM, then I would suggest &lt;a href=&quot;https://huggingface.co/Qwen/Qwen3-Coder-480B-A35B-Instruct&quot;&gt;Qwen-3-Coder&lt;/a&gt; as it is tuned for agentic coding.&lt;/p&gt;

&lt;p&gt;In your terminal, run the following command to get the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qwen3:8b&lt;/code&gt; model:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# On your workstation&lt;/span&gt;
ollama serve &amp;amp;
ollama run qwen3:8b
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now leave the Ollama application running in the background on your workstation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B: Run the Model Directly on z/OS with llama.cpp&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a fully self-contained solution, you can now run a model server directly on z/OS. Follow my &lt;a href=&quot;https://igortodorovskiibm.github.io/blog/2023/08/22/llama-cpp/&quot;&gt;previous blog&lt;/a&gt; on how to set that up.
One reason for running LLaMa.cpp locally on z/OS is security. Data on z/OS machines is typically sensitive, and as such, many clients choose to air-gap their systems. (Air-gapping isolates a computer or network from external connections). For sensitive industries like finance and healthcare, local AI models are critical for security by limiting exposure to external threats.&lt;/p&gt;

&lt;h3 id=&quot;step-2-the-zopen-mcp-server---our-custom-tool-bridge&quot;&gt;Step 2: The zopen MCP Server - Our Custom Tool Bridge&lt;/h3&gt;

&lt;p&gt;This is the component we built ourselves. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen-mcp-server&lt;/code&gt; is a Go application that acts as a translator. It listens for MCP requests from our AI agent (Crush) and converts them into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; commands. We use the official &lt;a href=&quot;https://github.com/modelcontextprotocol/go-sdk&quot;&gt;go mcp sdk&lt;/a&gt; to create the MCP server. Its core components are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcp.NewServer&lt;/code&gt; Function&lt;/strong&gt;: Creates the main server instance.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mcp.AddTool&lt;/code&gt; Function&lt;/strong&gt;: This is the key. It turns a Go function into a capability that the AI can discover and use. The tool’s description is crucial, as it tells the AI what the tool does.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; Function&lt;/strong&gt;: This handles startup and configuration, allowing the server to be run as a standalone executable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The end result is our zopen mcp server, available at &lt;a href=&quot;https://github.com/IgorTodorovskiIBM/zopen-mcp-server&quot;&gt;https://github.com/IgorTodorovskiIBM/zopen-mcp-server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install the server on z/OS, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go install&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# On z/OS&lt;/span&gt;
go &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;github.com/IgorTodorovskiIBM/zopen-mcp-server@v1.0.0
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;available-tools-in-the-zopen-mcp-server&quot;&gt;Available Tools in the zopen MCP server&lt;/h4&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen-mcp-server&lt;/code&gt; now supports the following commands:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_list&lt;/code&gt;: Lists information about zopen community packages.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_query&lt;/code&gt;: Lists local or remote info about zopen community packages.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_install&lt;/code&gt;: Installs one or more zopen community packages.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_remove&lt;/code&gt;: Removes installed zopen community packages.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_upgrade&lt;/code&gt;: Upgrades existing zopen community packages.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_info&lt;/code&gt;: Displays detailed information about a package.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_version&lt;/code&gt;: Displays the installed zopen version.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_init&lt;/code&gt;: Initializes the zopen environment.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_clean&lt;/code&gt;: Removes unused resources.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen_alt&lt;/code&gt;: Switches between different versions of a package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also run it remotely if you choose to run your Crush AI agent on your workstation.&lt;/p&gt;

&lt;h2 id=&quot;step-3-crush-your-terminal-agent---now-on-zos&quot;&gt;Step 3: Crush, Your Terminal Agent - Now on z/OS!&lt;/h2&gt;

&lt;p&gt;Crush is an AI agent that runs directly in your z/OS terminal.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href=&quot;https://github.com/charmbracelet/crush&quot;&gt;Crush repository&lt;/a&gt;, it offers a rich set of features:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Features&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Multi-Model&lt;/strong&gt; – Choose from a wide range of LLMs or add your own.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Built-in Tools&lt;/strong&gt; – Crush comes with support for a variety of tools out-of-the-box to interact with your system, including bash, download, edit, grep, ls, rg, and view.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Flexible&lt;/strong&gt; – Switch LLMs mid-session while preserving context.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Session-Based&lt;/strong&gt; – Maintain multiple work sessions and contexts per project.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;LSP-Enhanced&lt;/strong&gt; – Leverages LSPs for additional context, just like your editor.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Extensible&lt;/strong&gt; – Add capabilities via MCPs (http, stdio, and sse).  We use the stdio transport for increased security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now, thanks to the zopen community, &lt;strong&gt;Crush has been ported to z/OS&lt;/strong&gt;. You can install it using zopen:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# On z/OS&lt;/span&gt;
zopen &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;crush
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before we initialize crush, we need to configure it to use our local LLM.&lt;/p&gt;

&lt;h3 id=&quot;configuring-crush-the-crushjson-file&quot;&gt;Configuring Crush: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crush.json&lt;/code&gt; File&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crush.json&lt;/code&gt; file tells Crush how to connect all the pieces. The configuration will differ slightly depending on your chosen deployment model.&lt;/p&gt;

&lt;h4 id=&quot;configuration-for-workstation-model--zos-tools&quot;&gt;Configuration for Workstation Model + z/OS Tools&lt;/h4&gt;

&lt;p&gt;In this setup, Crush runs on z/OS, talks to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; server on z/OS, but gets its knowledge from the Ollama server running back on your workstation.&lt;/p&gt;

&lt;p&gt;First, create the file in the standard location:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt;  ~/.local/share/crush/
vim  ~/.local/share/crush/crush.json
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Copy and adjust these contents appropriately:&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://charm.land/crush.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;providers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ollama&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Ollama Workstation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;base_url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://&amp;lt;your_ip&amp;gt;:11434/v1/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;openai&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;models&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Qwen3 8B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;qwen3:8b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mcp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;zopen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stdio&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/path/on/zos/to/zopen-mcp-server&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;the-result-an-ai-powered-zos-workflow&quot;&gt;The Result: An AI-Powered z/OS Workflow!&lt;/h3&gt;

&lt;p&gt;With your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crush.json&lt;/code&gt; file in place, simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crush&lt;/code&gt; in your z/OS terminal. Crush will start, connect to your chosen LLM server, and launch your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen-mcp-server&lt;/code&gt; as a subprocess.&lt;/p&gt;

&lt;p&gt;From here, you can start giving it tasks to automate!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Can you clone the git repo https://github.com/git/git (with a depth of 1) and discover the build dependencies needed to compile it? Check if zopen already has these dependencies available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;style&gt;
  .fancybox {
    display: block;       /* make each anchor fill full width */
    margin: 0;            /* remove default margins */
    padding: 0;           /* remove any padding */
  }
  .fancybox img {
    display: block;       /* prevents inline spacing */
    margin: 0;            /* remove image margins */
    padding: 0;           /* remove image padding */
  }
&lt;/style&gt;

&lt;p&gt;&lt;a href=&quot;/blog/img/in-post/crush1.png&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/crush1.png&quot; alt=&quot;Crush&quot; title=&quot;Crush Image&quot; /&gt;
&lt;/a&gt;
&lt;a href=&quot;/blog/img/in-post/crush2.png&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/crush2.png&quot; alt=&quot;Crush&quot; title=&quot;Crush Image&quot; /&gt;
&lt;/a&gt;
&lt;a href=&quot;/blog/img/in-post/crush3.png&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/crush3.png&quot; alt=&quot;Crush&quot; title=&quot;Crush Image&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Crush already supports many tools, including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;view&lt;/code&gt;, which allow it to clone repositories and inspect their contents. It also leverages our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen-mcp-server&lt;/code&gt; to check package availability on z/OS.&lt;/p&gt;

&lt;p&gt;In this example, Crush cloned the Git repository, inspected the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSTALL&lt;/code&gt; file, and produced a dependency analysis:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Core dependencies found in zopen&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zlib&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Perl&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libcurl&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gettext&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Optional dependencies missing or partially available&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expat&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tcl/tk&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmlto&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Documentation tools not explicitly packaged in zopen&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asciidoc&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;makeinfo&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docbook2X&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dblatex&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docbook-xsl&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crush was able to summarize this automatically, saving the manual effort of digging through build scripts.&lt;/p&gt;

&lt;p&gt;Pretty awesome!&lt;/p&gt;

&lt;p&gt;Feel free to experiment. You can ask it anything, from understanding your codebase to making changes to it!&lt;/p&gt;

&lt;h1 id=&quot;next-steps-an-ai-assistant-for-porting-apps&quot;&gt;Next Steps: An AI Assistant for Porting Apps&lt;/h1&gt;
&lt;p&gt;While package management is a great first step, the real power of agentic AI lies in automating complex, knowledge-intensive tasks. My next goal is to tackle one of the most time-consuming parts of my workflow: &lt;strong&gt;porting new open-source applications to z/OS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Agentic AI can also assist with debugging issues during the porting process—and much more. Exciting times ahead!&lt;/p&gt;

&lt;p&gt;Thank you to Mike Fulton, Bill O’Farrell and Andrew Sica for reviewing this post and providing valuable feedback!&lt;/p&gt;
</description>
        <pubDate>Tue, 02 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2025/09/02/agentic-ai/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2025/09/02/agentic-ai/</guid>
        
        <category>z/OS</category>
        
        <category>zopen</category>
        
        <category>AI</category>
        
        <category>Agentic</category>
        
        <category>Crush</category>
        
        <category>MCP</category>
        
        <category>Ollama</category>
        
        
      </item>
    
      <item>
        <title>Faster builds on z/OS with ccache</title>
        <description>&lt;p&gt;As a developer in the &lt;a href=&quot;https://zopen.community/&quot;&gt;zopen community&lt;/a&gt; (a z/OS Open Source Community), I’ve lost what feels like countless hours waiting for builds to finish. The build-time bottleneck is especially painful in an automated CI/CD pipeline, where I’m waiting on a critical fix to be built, verified, and deployed. I personally don’t enjoy waiting, and neither do our users. This drove me to find a better way, and my search led me to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt;, a tool that caches compiler output to speed up rebuilds. The caching approach intrigued me, so I decided to &lt;a href=&quot;https://github.com/zopencommunity/ccacheport&quot;&gt;port ccache&lt;/a&gt; to z/OS and integrate it into the zopen community to see if it could help.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll cover the basics of ccache, how to get it running on z/OS, how to use it effectively, and most importantly, how it can &lt;strong&gt;cut your build times by up to 95%&lt;/strong&gt;!&lt;/p&gt;

&lt;h3 id=&quot;what-is-ccache-and-why-should-i-care&quot;&gt;What is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; and Why Should I Care?&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; is a compiler cache. It speeds up recompilation by caching the results of previous compilations. When rebuilding software, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; provides these cached outputs instead of re-running the compiler. This results in a much faster build turnaround-time.&lt;/p&gt;

&lt;h3 id=&quot;how-ccache-works&quot;&gt;How &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; Works&lt;/h3&gt;

&lt;p&gt;The beauty of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; is its simplicity. It acts as a ‘wrapper’ that intercepts calls to your real compiler (in my case, the &lt;strong&gt;IBM Open XL C/C++ clang compiler&lt;/strong&gt;). It then quickly determines whether it has a valid, cached result (usually a compiled object) from a previous identical compilation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Process&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Analysis and Hashing:&lt;/strong&gt; When your build process calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt;, it analyzes the command and runs the C/C++ preprocessor on your source file. This is a key step because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; hashes the preprocessed output, meaning &lt;strong&gt;it’s fully aware of header file changes&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;“Cache Hit”:&lt;/strong&gt; If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; finds a previously stored object file that matches this exact hash, it copies the cached result to the correct location. This is the fast path.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;“Cache Miss”:&lt;/strong&gt; If no match is found, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; calls the real compiler to do the work. Upon success, it saves the new object file to its on-disk cache, ready for the next time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more in-depth details, visit the official &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; website: &lt;strong&gt;&lt;a href=&quot;https://ccache.dev&quot;&gt;https://ccache.dev&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;using-ccache-on-zos&quot;&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; on z/OS&lt;/h3&gt;

&lt;h4 id=&quot;1-installing-ccache&quot;&gt;1. Installing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Begin by installing or updating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; on your system using the &lt;a href=&quot;https://github.com/zopencommunity/meta&quot;&gt;zopen package manager&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;ccache 
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;leveraging-ccache&quot;&gt;Leveraging Ccache&lt;/h4&gt;

&lt;p&gt;There are several ways to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt;. You can use it for a single compilation, integrate it into your own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Makefile&lt;/code&gt;, or use the seamless support within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; build framework.&lt;/p&gt;

&lt;h4 id=&quot;simple-usage-caching-a-single-file&quot;&gt;Simple Usage: Caching a Single File&lt;/h4&gt;

&lt;p&gt;For a quick test or a one-off compilation, you can simply prefix your compiler command with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Instead of: clang -c myprogram.c -o myprogram.o&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Use:&lt;/span&gt;
ccache clang &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; myprogram.c &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; myprogram.o
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first time you run this, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; will compile and cache the result. The second time, it will be nearly instant.&lt;/p&gt;

&lt;h4 id=&quot;integration-into-makefiles-using-ccache-in-a-makefile&quot;&gt;Integration into Makefiles: Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; in a Makefile&lt;/h4&gt;

&lt;p&gt;For any personal or non-zopen project that you build with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt;, the most common way to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; is to set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CC&lt;/code&gt; variable in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Makefile&lt;/code&gt;. This tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; for all C compilations.&lt;/p&gt;

&lt;div class=&quot;language-makefile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# In your Makefile
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CC&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; ccache clang
&lt;span class=&quot;nv&quot;&gt;CFLAGS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-m64&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-O3&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;my_program&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main.o utils.o&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$(CC)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$(CFLAGS)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; my_program main.o utils.o

&lt;span class=&quot;nl&quot;&gt;main.o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main.c&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$(CC)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$(CFLAGS)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; main.c

&lt;span class=&quot;nl&quot;&gt;utils.o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;utils.c&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$(CC)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$(CFLAGS)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; utils.c
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With this setup, every time you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt;, the compilation steps will automatically benefit from caching.&lt;/p&gt;

&lt;h3 id=&quot;benchmarking-ccache-with-the-zopen-build-framework&quot;&gt;Benchmarking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; with the zopen Build Framework&lt;/h3&gt;

&lt;p&gt;While the manual methods are great, I wanted to test &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; in a fully integrated scenario for zopen. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; project provides a build framework (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen build&lt;/code&gt;) that automates the entire build process, and it now has built-in support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; through a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--ccache&lt;/code&gt; flag. I used the &lt;strong&gt;GNU &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt;&lt;/strong&gt; port (&lt;a href=&quot;https://github.com/zopencommunity/bashport&quot;&gt;bashport&lt;/a&gt;) as my test project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first step was to clone the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bashport&lt;/code&gt; repository from the zopen community.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;git clone https://github.com/zopencommunity/bashport.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;bashport
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The Experiment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My experiment was designed to mimic a CI/CD workflow where the workspace is cleaned before each run. I performed three builds:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A baseline build without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;A build with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--ccache&lt;/code&gt; on an empty cache.&lt;/li&gt;
  &lt;li&gt;A second build with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--ccache&lt;/code&gt; on a populated cache.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;1. The Baseline Test (Without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, I timed a standard, full build using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen build&lt;/code&gt; with verbose flags.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Starting baseline &apos;zopen build&apos;...&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;zopen build &lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Baseline Full Build Time:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;real    8m43.963s&lt;/code&gt; (approximately &lt;strong&gt;524 seconds&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;The zopen build framework, which times the core build phase separately from configure and install, reported: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERBOSE: build completed in 237 seconds.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This nearly 9-minute full-build time, and more specifically the 237-second build phase, became my baseline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; Test (Cold Cache)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, I removed the build artifacts to simulate a clean workspace and ran the build again, this time adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--ccache&lt;/code&gt; flag. Before this step, it’s important to have the necessary tools.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; bash-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# remove the bash source/build directory&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;zopen build &lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--ccache&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Cold &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; Build Time:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;real    9m16.050s&lt;/code&gt; (approximately &lt;strong&gt;556 seconds&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;The zopen build framework, which times the core build phase separately from configure and install, reported: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERBOSE: build completed in 251 seconds.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the results show, the very first build with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; was slightly &lt;em&gt;slower&lt;/em&gt; than the baseline. This is expected! This is the one-time cost of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; hashing every file and populating its cache for the first time. The real magic happens next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; Test (Warm Cache):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the key test. It simulates all subsequent builds in a CI pipeline or a developer’s clean rebuild after the cache is populated. I again removed the build artifacts and re-ran the exact same command.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; bash-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# remove the bash source/build directory&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Starting &apos;zopen build --ccache&apos; on a warm cache...&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;zopen build &lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--ccache&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Warm &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; Build Time:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;real    4m2.766s&lt;/code&gt; (approximately &lt;strong&gt;243 seconds&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;The zopen build framework reported: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERBOSE: build completed in 12 seconds.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The results were excellent! The total time dropped from &lt;strong&gt;8m43s&lt;/strong&gt; to &lt;strong&gt;4m2s&lt;/strong&gt;. That’s a &lt;strong&gt;54% reduction (more than 2x faster)&lt;/strong&gt; in the total time I had to wait.&lt;/p&gt;

&lt;p&gt;The framework’s build timer tells a better story: the core build phase, which was &lt;strong&gt;237 seconds&lt;/strong&gt; in the baseline, dropped to just &lt;strong&gt;12 seconds&lt;/strong&gt;—a reduction of &lt;strong&gt;95%&lt;/strong&gt;! This shows that with a warm cache, the time-consuming compilation step was almost entirely eliminated, leaving only the much faster linking step in the build phase. While the configure and install phases took a similar amount of time in each run, the massive speedup in the build phase drove this incredible overall improvement.&lt;/p&gt;

&lt;h3 id=&quot;managing-and-monitoring-your-cache&quot;&gt;Managing and Monitoring Your Cache&lt;/h3&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; is great, but knowing how to manage it is also critical.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Checking Statistics:&lt;/strong&gt; To see how effective &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ccache&lt;/code&gt; is, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-s&lt;/code&gt; flag. It provides a human-readable summary of cache hits, misses, cache size, and more.
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  ccache &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Clearing the Cache:&lt;/strong&gt; To start completely fresh, you can clear the entire cache.
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  ccache &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Setting Cache Size:&lt;/strong&gt; You can control the maximum size of the cache. For example, to set a 5 GB limit:
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  ccache &lt;span class=&quot;nt&quot;&gt;-M&lt;/span&gt; 5G
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;future-considerations&quot;&gt;Future Considerations&lt;/h3&gt;

&lt;p&gt;While ccache delivers clear performance gains for C/C++ with modern compilers like Clang, traditional z/OS compilers—such as those for COBOL or PL/I—aren’t supported.&lt;/p&gt;

&lt;p&gt;This raises important questions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Should we explore caching strategies for legacy languages on z/OS?&lt;/li&gt;
  &lt;li&gt;What impact could this have on build speed, especially in integrated CI/CD pipelines?&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Fri, 11 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2025/07/11/speed-up-with-ccache/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2025/07/11/speed-up-with-ccache/</guid>
        
        <category>ccache</category>
        
        <category>performance</category>
        
        <category>build</category>
        
        <category>z/OS</category>
        
        <category>zopen</category>
        
        
      </item>
    
      <item>
        <title>Optimizing Git Performance on z/OS</title>
        <description>&lt;h2 id=&quot;optimizing-git-performance&quot;&gt;Optimizing Git Performance&lt;/h2&gt;

&lt;p&gt;As an open source developer for the &lt;a href=&quot;https://zopen.community/&quot;&gt;zopen community&lt;/a&gt;, I rely heavily on Git for version control.  But, as repositories grow in size and history, my git operations often slow down to the point where they can impact my daily workflow, slow down CI/CD pipelines, and ultimately affect my productivity as a developer.&lt;/p&gt;

&lt;p&gt;I wanted to make sure I was doing everything I can to optimize my Git worfklow, So recently, I investigated various Git performance optimization techniques, and I decided to put them to the test.&lt;/p&gt;

&lt;p&gt;My goal was to understand the practical impact of these strategies on z/OS by using git from IBM Open Enterprise Foundation for z/OS.&lt;/p&gt;

&lt;p&gt;We’ll start by exploring the strategies that provided the most significant performance improvements first.&lt;/p&gt;

&lt;h3 id=&quot;1-data-reduction-strategies&quot;&gt;1. Data Reduction Strategies&lt;/h3&gt;

&lt;p&gt;The idea behind “data reduction” strategies is simple: if we can reduce the amount of data Git has to process, we can improve Git’s performance.&lt;/p&gt;

&lt;h4 id=&quot;shallow-clones-reducing-the-history---depth1&quot;&gt;Shallow Clones: Reducing the History (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--depth=1&lt;/code&gt;)&lt;/h4&gt;

&lt;p&gt;Shallow clones are a great way to speed up clones when you don’t need the full commit history. By cloning with a limited depth, you eliminate the commit history and thereby reduce the amount of data transferred, making cloning much faster. This is extremely useful in CI/CD pipelines or for quickly getting the latest codebase where the commit history is not relevant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Reduced Cloning Time:&lt;/strong&gt;  Save time, especially for repositories with a long history.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lower Bandwidth:&lt;/strong&gt;  Less data to transfer&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reduced Disk Footprint:&lt;/strong&gt;  Shallow clones take up significantly less disk space.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Experiment on Perl 5:&lt;/strong&gt;  As an experiment, I cloned the Perl 5 repository (&lt;a href=&quot;https://github.com/Perl/perl5&quot;&gt;https://github.com/Perl/perl5&lt;/a&gt;). This repository contains the source code for the latest Perl language implementation. First I cloned perl5 with a full clone and then with a shallow clone (–depth=1 which downloads the latest commit only).  I ran these tests on a z/OS 3.1 z15 machine.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Baseline: Full Clone&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;git clone https://github.com/Perl/perl5 perl5-full-clone

&lt;span class=&quot;c&quot;&gt;# Shallow Clone with depth 1&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;git clone &lt;span class=&quot;nt&quot;&gt;--depth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 https://github.com/Perl/perl5 perl5-shallow-clone
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The results were as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;# Baseline: Full Clone
real     0m46.611s
user     1m16.479s
sys      0m25.493s

# Shallow Clone with depth 1
real     0m4.447s
user     0m2.328s
sys      0m0.776s
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Shallow cloning the Perl 5 repository showed a dramatic improvement! The clone time was reduced from over &lt;strong&gt;46 seconds&lt;/strong&gt; to just over &lt;strong&gt;4 seconds&lt;/strong&gt;, a reduction of over &lt;strong&gt;90%&lt;/strong&gt;! The repository size was also significantly reduced as only the latest commit was downloaded, instead of the entire history. For scenarios where commit history isn’t immediately needed, shallow clones make a clear difference, especially in CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspecting the Size of a Shallow Clone&lt;/strong&gt; 
To gain further insight into the volume of data being processed during your shallow clones, you can execute the following command within your Git repository: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git count-objects -vH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After performing a clone, navigate into the newly created repository directory (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd perl5-shallow-clone&lt;/code&gt;) and run the command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;git count-objects &lt;span class=&quot;nt&quot;&gt;-vH&lt;/span&gt; 
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command will provide a human-readable summary of the number and size of the various Git objects in your repository’s object database.&lt;/p&gt;

&lt;p&gt;Now let’s compare the output from a full clone vs a shallow clone:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;# Full clone output:
count: 0
size: 0 bytes
in-pack: 530133
packs: 1
size-pack: 319.96 MiB
prune-packable: 0
garbage: 0
size-garbage: 0 bytes

# Shallow clone output:
count: 0
size: 0 bytes
in-pack: 7703
packs: 1
size-pack: 22.09 MiB
prune-packable: 0
garbage: 0
size-garbage: 0 bytes
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As shown, a shallow clone drastically reduces the data downloaded by fetching only recent commits instead of the entire history. This is reflected in the in-pack count, which drops from &lt;strong&gt;530,133 objects&lt;/strong&gt; in a full clone to just &lt;strong&gt;7,703 objects&lt;/strong&gt; in a shallow clone. Since fewer commits, trees, and blobs are included, the total size-pack also shrinks significantly, from &lt;strong&gt;319.96 MiB&lt;/strong&gt; to &lt;strong&gt;22.09 MiB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This reduction happens because Git stores repository data in packfiles, which group and compress objects for efficiency. In a full clone, all historical commits and their associated objects are included, leading to a large size-pack. In contrast, a shallow clone fetches only recent objects, leaving out older history, resulting in fewer packed objects and a much smaller repository footprint.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Metric&lt;/th&gt;
      &lt;th&gt;Full Clone&lt;/th&gt;
      &lt;th&gt;Shallow Clone&lt;/th&gt;
      &lt;th&gt;Reduction&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Clone Time&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;46s&lt;/td&gt;
      &lt;td&gt;4s&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;91%&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;319MB&lt;/td&gt;
      &lt;td&gt;22MB&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;93%&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Objects&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;530,133&lt;/td&gt;
      &lt;td&gt;7,703&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;98.5%&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;avoiding-downloading-large-binaries---filterblobnone&quot;&gt;Avoiding Downloading Large Binaries (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--filter=blob:none&lt;/code&gt;)&lt;/h3&gt;

&lt;p&gt;Repositories can sometimes accumulate large binary files over time. If these binaries (e.g pax files, object files, executables) are not needed for every operation or during development, you can instruct Git to filter these out during the cloning process. This can help reduce bandwidth, disk space and clone time.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--filter=blob:none&lt;/code&gt; option prevents Git from downloading any large file blobs.&lt;/p&gt;

&lt;p&gt;I cloned the Perl 5 repository again, this time using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--filter=blob:none&lt;/code&gt; option to exclude binary blobs.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;git clone &lt;span class=&quot;nt&quot;&gt;--filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;blob:none  git@github.com:Perl/perl5.git
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The results were as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;real     0m15.333s
user     0m9.057s
sys      0m3.019s
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--filter=blob:none&lt;/code&gt; reduced the clone time to approximately &lt;strong&gt;15 seconds&lt;/strong&gt;, significantly faster than the baseline full clone of 46 seconds. This is a great way to reduce clone time, while maintaining history for repositories where binaries are not essential for maintenance.&lt;/p&gt;

&lt;h3 id=&quot;cloning-only-a-single-branch-and-avoiding-tags---no-tags-and---single-branch&quot;&gt;Cloning Only a Single Branch and Avoiding Tags (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-tags&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--single-branch&lt;/code&gt;)&lt;/h3&gt;

&lt;p&gt;By default, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone&lt;/code&gt; clones all branches and tags.  If you are only interested in a single branch, and tags are not immediately necessary, cloning only a single branch and excluding tags can reduce the amount of data transferred and speed up the clone operation. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-tags&lt;/code&gt; option skips downloading tags, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--single-branch&lt;/code&gt; clones only the specified branch.&lt;/p&gt;

&lt;p&gt;I cloned the Perl 5 repository again, this time using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-tags&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--single-branch&lt;/code&gt; options.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;git clone  &lt;span class=&quot;nt&quot;&gt;--no-tags&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--single-branch&lt;/span&gt; git@github.com:Perl/perl5.git
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The results were:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;real     0m34.867s
user     1m2.789s
sys      0m20.930s
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cloning a single branch without tags took approximately &lt;strong&gt;35 seconds&lt;/strong&gt;, which is faster than the full clone baseline, but not as fast as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--filter=blob:none&lt;/code&gt;.  However, when combined with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--filter=blob:none&lt;/code&gt;, the total clone time was reduced to &lt;strong&gt;13s&lt;/strong&gt;.&lt;/p&gt;

&lt;h4 id=&quot;sparse-checkouts-clone-what-you-need-git-sparse-checkout&quot;&gt;Sparse Checkouts: Clone What You Need (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git sparse-checkout&lt;/code&gt;)&lt;/h4&gt;

&lt;p&gt;Sparse checkouts allow you to download only a specific subset of directories from a repository into your working directory.  This is great for monorepos or large projects where you only work within certain modules or components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Reduced Working Directory Size:&lt;/strong&gt;  Save disk space by only checking out necessary files.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Faster &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt; and Checkout Operations:&lt;/strong&gt;  Git has less data to track and manage in your working directory, improving command speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a sparse checkout on the Perl 5 repository, I wanted to exclude the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cpan/&lt;/code&gt; directory, which contains CPAN module-related code and is approximately 26mb in size. I used the following script:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# sparse-checkout.sh&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Initialize an empty repository&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; perl5-sparse-empty-test
&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;perl5-sparse-empty-test
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;perl5-sparse-empty-test
git init

&lt;span class=&quot;c&quot;&gt;# Add the Perl 5 repository as a remote&lt;/span&gt;
git remote add origin &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;https://github.com/Perl/perl5]&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;https://github.com/Perl/perl5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Ignore the cpan directory&lt;/span&gt;
git config core.sparseCheckoutCone &lt;span class=&quot;nb&quot;&gt;false
&lt;/span&gt;git sparse-checkout init &lt;span class=&quot;nt&quot;&gt;--no-cone&lt;/span&gt;
git sparse-checkout &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/*&quot;&lt;/span&gt;
git sparse-checkout add &lt;span class=&quot;s1&quot;&gt;&apos;!cpan&apos;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# ignore cpan dir&lt;/span&gt;

git fetch origin blead

git checkout blead
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running this script with sparse checkout commands yielded a total time of:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;real     0m42.361s
user     1m7.524s
sys      0m22.508s
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When the sparse-checkout commands are commented out, essentially performing a full clone, the time taken was:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;real     0m43.325s
user     1m8.081s
sys      0m22.694s
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While the time difference isn’t dramatically significant in this specific test, sparse checkout successfully saved approximately &lt;strong&gt;26mb&lt;/strong&gt; of data by excluding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cpan&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3 id=&quot;working-tree-encoding-consider-the-conversion-costs-gitattributes&quot;&gt;Working-Tree-Encoding: Consider the Conversion Costs (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitattributes&lt;/code&gt;)&lt;/h3&gt;

&lt;p&gt;If your project uses working-tree-encoding or zos-working-tree-encoding, be aware of the potential performance overhead. Encoding conversions handled by iconv can add CPU processing time, especially when global wildcards are used.&lt;/p&gt;

&lt;p&gt;To test this, I created a fork of the Perl 5 repository with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitattributes&lt;/code&gt; file that would apply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zos-working-tree-encoding=ibm-1047&lt;/code&gt; to all text files:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/IgorTodorovskiIBM/perl5&quot;&gt;https://github.com/IgorTodorovskiIBM/perl5&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;# .gitattributes
* text zos-working-tree-encoding=ibm-1047
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cloning this repository:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;git clone git@github.com:IgorTodorovskiIBM/perl5.git
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The encoding conversion, when applied globally with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; wildcard, added about 3 seconds to the total clone time compared to cloning the original repository without the encoding attribute. While 3 seconds might seem small, it’s another factor to consider, especially in scenarios involving large repositories or frequent Git operations. For projects using working-tree-encoding, consider the scope of encoding conversions to reduce potential performance impacts.&lt;/p&gt;

&lt;p&gt;However, an important counterpoint to skipping conversion at the Git level means each compile step must handle encoding conversion instead. For languages like C, which support auto-conversion of source files, this can introduce overhead every time a file is compiled. This ongoing cost might outweigh the one-time Git conversion delay.&lt;/p&gt;

&lt;h3 id=&quot;profiling-and-diagnostics-git_trace1-and-git_performance1&quot;&gt;Profiling and Diagnostics (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_TRACE=1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_PERFORMANCE=1&lt;/code&gt;)&lt;/h3&gt;

&lt;p&gt;Git’s diagnostic environment variables such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_TRACE&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_PERFORMANCE&lt;/code&gt; are great ways for identifying performance bottlenecks within Git operations. By enabling these variables, Git generates detailed logs that can reveal which specific steps are consuming the most time and resources.&lt;/p&gt;

&lt;p&gt;To enable tracing and performance logging, set the corresponding environment variables before running your Git commands.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_TRACE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_PERFORMANCE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After setting these variables, execute the Git command you want to analyze. Git will output detailed trace information to stderr.&lt;/p&gt;

&lt;p&gt;For example, to trace a clone operation:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_TRACE_PERFORMANCE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 &lt;span class=&quot;nv&quot;&gt;GIT_TRACE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
12:33:53.398786 exec-cmd.c:267          trace: resolved executable &lt;span class=&quot;nb&quot;&gt;dir&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/bin
12:33:53.399026 git.c:476               trace: built-in: git clone https://github.com/Perl/perl5
Cloning into &lt;span class=&quot;s1&quot;&gt;&apos;perl5&apos;&lt;/span&gt;...
12:33:53.406725 run-command.c:667       trace: run_command: git remote-https origin https://github.com/Perl/perl5
12:33:53.406752 run-command.c:759       trace: start_command: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core/git remote-https origin https://github.com/Perl/perl5
12:33:53.433854 exec-cmd.c:267          trace: resolved executable &lt;span class=&quot;nb&quot;&gt;dir&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core
12:33:53.434307 git.c:769               trace: &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt;: git-remote-https origin https://github.com/Perl/perl5
12:33:53.434315 run-command.c:667       trace: run_command: git-remote-https origin https://github.com/Perl/perl5
12:33:53.434338 run-command.c:759       trace: start_command: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core/git-remote-https origin https://github.com/Perl/perl5
12:33:53.468060 exec-cmd.c:267          trace: resolved executable &lt;span class=&quot;nb&quot;&gt;dir&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core
12:33:53.938639 run-command.c:667       trace: run_command: git index-pack &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--fix-thin&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--keep=fetch-pack 33562439 on ZOSCAN2B&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--check-self-contained-and-connected&lt;/span&gt;
12:33:53.938676 run-command.c:759       trace: start_command: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core/git index-pack &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--fix-thin&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--keep=fetch-pack 33562439 on ZOSCAN2B&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--check-self-contained-and-connected&lt;/span&gt;
12:33:53.965929 exec-cmd.c:267          trace: resolved executable &lt;span class=&quot;nb&quot;&gt;dir&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core
12:33:53.966507 git.c:476               trace: built-in: git index-pack &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--fix-thin&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--keep=fetch-pack 33562439 on ZOSCAN2B&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--check-self-contained-and-connected&lt;/span&gt;
remote: Enumerating objects: 613690, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Counting objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;16/16&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Compressing objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;16/16&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Total 613690 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, reused 5 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, pack-reused 613674 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;from 1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
12:34:02.103539 trace.c:417             performance: 8.669476706 s: git &lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core/git remote-https origin https://github.com/Perl/perl5
Receiving objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;613690/613690&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, 340.21 MiB | 42.31 MiB/s, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Resolving deltas: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;464433/464433&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
12:34:31.897026 trace.c:417             performance: 37.930887480 s: git &lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core/git index-pack &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--fix-thin&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--keep=fetch-pack 33562439 on ZOSCAN2B&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--check-self-contained-and-connected&lt;/span&gt;
12:34:31.898222 run-command.c:667       trace: run_command: git rev-list &lt;span class=&quot;nt&quot;&gt;--objects&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--not&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--alternate-refs&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--progress=Checking connectivity&apos;&lt;/span&gt;
12:34:31.898281 run-command.c:759       trace: start_command: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core/git rev-list &lt;span class=&quot;nt&quot;&gt;--objects&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--not&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--alternate-refs&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--progress=Checking connectivity&apos;&lt;/span&gt;
12:34:31.932503 exec-cmd.c:267          trace: resolved executable &lt;span class=&quot;nb&quot;&gt;dir&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core
12:34:31.933121 git.c:476               trace: built-in: git rev-list &lt;span class=&quot;nt&quot;&gt;--objects&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--not&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--alternate-refs&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--progress=Checking connectivity&apos;&lt;/span&gt;
12:34:31.933623 trace.c:417             performance: 0.000879117 s: git &lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt;: /home/itodoro/zopen/usr/local/zopen/git/git-heads.v2.48.1.20250131_080517.zos/libexec/git-core/git rev-list &lt;span class=&quot;nt&quot;&gt;--objects&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--stdin&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--not&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--alternate-refs&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--progress=Checking connectivity&apos;&lt;/span&gt;
12:34:31.999553 unpack-trees.c:2010     performance: 0.022947339 s:   traverse_trees
Updating files: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6895/6895&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
12:34:33.150223 unpack-trees.c:512      performance: 1.150647878 s:   check_updates
12:34:33.157202 cache-tree.c:492        performance: 0.006916968 s:   cache_tree_update
12:34:33.157222 unpack-trees.c:2107     performance: 1.180711913 s:  unpack_trees
12:34:33.160126 read-cache.c:3115       performance: 0.002893479 s:  write index, changed mask &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 2a
12:34:33.160601 trace.c:417             performance: 39.761582911 s: git &lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt;: git clone https://github.com/Perl/perl5

real    0m39.793s
user    1m12.404s
sys     0m24.135s
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can now examine the log output to identify performance bottlenecks and areas for potential optimization.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Whenever possible, use shallow clones with a depth of 1 as it provides a significant performance benefit.&lt;/p&gt;

&lt;p&gt;For those working within z/OS, a more detailed guide outlining specific performance recommendations can be found in the Git on z/OS guide: &lt;a href=&quot;https://github.com/zopencommunity/gitport?tab=readme-ov-file#git-performance-considerations&quot;&gt;https://github.com/zopencommunity/gitport?tab=readme-ov-file#git-performance-considerations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Git also provides various performance-related configuration settings, such as adjusting the number of parallel threads and other optimizations: &lt;a href=&quot;https://git-scm.com/docs/git-config&quot;&gt;Git Configuration Options&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any other tips or strategies for improving Git performance, I’d love to hear them!&lt;/p&gt;

&lt;p&gt;A special thanks to Mike Fulton for his contributions to this article.&lt;/p&gt;

&lt;h3 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ibm.com/products/open-enterprise-foundation-zos&quot;&gt;IBM Open Enterprise Foundation for z/OS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://community.ibm.com/community/user/ibmz-and-linuxone/blogs/ian-mitchell1/2024/03/06/worried-about-cloning-your-large-z-code-repo?communityKey=f461c55d-159c-4a94-b708-9f7fe11d972b&quot;&gt;Worried about cloning your large Z code Git repo?
&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/&quot;&gt;Get up to speed with partial clone and shallow clone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 19 Mar 2025 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2025/03/19/git-perf/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2025/03/19/git-perf/</guid>
        
        <category>git</category>
        
        <category>performance</category>
        
        <category>z/OS</category>
        
        
      </item>
    
      <item>
        <title>Performance and Memory Insights: Tracing with Clang and Perfetto</title>
        <description>&lt;h1 id=&quot;enabling-tracing-with-clang-on-zos&quot;&gt;Enabling Tracing with Clang on z/OS&lt;/h1&gt;

&lt;p&gt;As a member of the &lt;a href=&quot;https://github.com/zopencommunity&quot;&gt;zopen community&lt;/a&gt;, I get the opportunty to port a wide range of open-source tools to z/OS. It’s always exciting when you get a tool running smoothly on z/OS, especially when it makes your workflow easier. However, even in cases where everything seems to be working fine, &lt;strong&gt;performance issues&lt;/strong&gt; or &lt;strong&gt;memory leaks&lt;/strong&gt; can still creep in. While IPCS is great for deep-dive debugging, I’ve never been able to become proficient at it. I wanted something that was easier to use and more visual. In this post, I’ll walk you through how I addressed this using Clang’s instrumentation features and &lt;a href=&quot;https://perfetto.dev/&quot;&gt;Perfetto&lt;/a&gt;’s trace visualization and show how you can easily apply them in your projects.&lt;/p&gt;

&lt;p&gt;I start first with a dive into how I generated the trace data. If you’re not interested in the tracing implementation, you can skip over to &lt;a href=&quot;#visualizing-the-trace-data-using-perfetto&quot;&gt;Visualizing the Trace data using Perfetto&lt;/a&gt; for how to leverage it.&lt;/p&gt;

&lt;p&gt;For the impatient, here’s a quick look at the end result:
&lt;a href=&quot;/blog/img/in-post/perfetto.gif&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/perfetto.gif&quot; alt=&quot;Perfetto Image&quot; title=&quot;Perfetto Image&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;compiler-tracing-with-clang&quot;&gt;Compiler Tracing with Clang&lt;/h2&gt;

&lt;p&gt;Before I started on this project, I did some research and discovered Clang’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-finstrument-functions&lt;/code&gt; feature, available in &lt;a href=&quot;https://www.ibm.com/products/xl-cpp-compiler-zos&quot;&gt;IBM Open XL C/C++&lt;/a&gt;. This compiler option automatically inserts instrumentation code at each function entry and exit point, making it possible to collect detailed tracing metrics. With the instrumented code, I could easily capture function names, timestamps, and other data, all of which are essential for profiling performance and understanding call flow. Another route I could have taken would have been to port the &lt;a href=&quot;https://llvm.org/docs/XRay.html&quot;&gt;LLVM XRay instrumentation library&lt;/a&gt;, but that seemed like a lot of effort for an experimental project.&lt;/p&gt;

&lt;h3 id=&quot;integrating-with-zoslib&quot;&gt;Integrating with zoslib&lt;/h3&gt;

&lt;p&gt;Clang’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-finstrument-functions&lt;/code&gt; feature only requires two functions to be implemented: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__cyg_profile_func_enter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__cyg_profile_func_exit&lt;/code&gt;. By leveraging these, I was able to capture the function names using the Program Prologue Area (PPA1), along with the timestamps, process IDs (PIDs), and thread IDs (TIDs).&lt;/p&gt;

&lt;p&gt;My initial implementation was simple and fairly effective:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;__attribute__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;no_instrument_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;write_perf_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;phase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pthread_mutex_lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__prof_mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;___file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ph&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: %d, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tid&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: %lu&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;phase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gettid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_timens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pthread_mutex_unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__prof_mutex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;__attribute__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;no_instrument_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__cyg_profile_func_enter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;this_fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;__stack_info&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur_dsa&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;__iterate_stack_and_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur_dsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;write_perf_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;__attribute__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;no_instrument_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__cyg_profile_func_exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;this_fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call_site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;__stack_info&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur_dsa&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;__iterate_stack_and_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cur_dsa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;write_perf_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;E&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After successfully testing the prototype, I integrated it into zoslib, a foundational open-source library for building C/C++ projects on z/OS.&lt;/p&gt;

&lt;p&gt;Zoslib is linked into 99% of the C/C++ projects within the zopen community. If you want to learn more about zoslib, check out my other blog &lt;a href=&quot;https://igortodorovskiibm.github.io/blog/2024/02/12/zoslib-library/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;standardizing-trace-data-format&quot;&gt;Standardizing Trace Data Format&lt;/h4&gt;

&lt;p&gt;While the prototype was definitely a step up over what I had before, it was still difficult and time-consuming to analyze the data. I needed to generate the trace data into a more consumable format. One such standardized format is the &lt;a href=&quot;https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview&quot;&gt;Chrome Tracing JSON format&lt;/a&gt;. This format offers several advantages:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Readability on z/OS via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq&lt;/code&gt; command since the trace data will be in json.&lt;/li&gt;
  &lt;li&gt;Compatibility with Chrome’s built-in tracing feature and &lt;a href=&quot;https://perfetto.dev/&quot;&gt;Perfetto open source&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;trace-data-structure&quot;&gt;Trace Data Structure&lt;/h5&gt;

&lt;p&gt;The Chrome Tracing format stores each function call as a JSON object with these attributes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Function name&lt;/li&gt;
  &lt;li&gt;Timestamp&lt;/li&gt;
  &lt;li&gt;PID&lt;/li&gt;
  &lt;li&gt;TID&lt;/li&gt;
  &lt;li&gt;Phase (“B” for function entry, “E” for function exit)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make the traces more manageable, especially for long-running applications, I implemented automatic gzip compression. After the trace is generated, a check ensures that gzip is available, and if so, the trace file is compressed. This keeps the file sizes under control, but if gzip fails for some reason, the trace file remains uncompressed, and the application continues as normal.&lt;/p&gt;

&lt;h3 id=&quot;next-step---tracking-memory-allocations&quot;&gt;Next Step - Tracking Memory Allocations&lt;/h3&gt;

&lt;p&gt;Another pain point for me on z/OS was tracing memory allocations. IPCS is powerful, but again I needed something more accessible for memory footprint analysis. So, I figured why not extend this tracing to handle memory management too?&lt;/p&gt;

&lt;p&gt;So that’s what I did. I implemented custom overrides for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;malloc&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;free&lt;/code&gt; to trace and track heap memory spikes and allocation patterns.&lt;/p&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;current implementation&lt;/strong&gt; is available in zoslib &lt;a href=&quot;https://github.com/ibmruntimes/zoslib/blob/zopen2/src/zos-instrumentation.cc&quot;&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here’s an example of the generated json for git on z/OS:&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;traceEvents&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;PERF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;zoslib_env_hook&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582917421&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;malloc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582922816&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;free&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582928703&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;malloc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582929642&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;free&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582934379&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;malloc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582935145&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;free&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582940290&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;malloc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582941020&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;free&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582942273&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;malloc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582942975&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;free&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582949534&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;delta_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;total_memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;PERF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;zoslib_env_hook&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;E&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582950330&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;PERF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ph&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83893699&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;582970044&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;visualizing-the-trace-data-using-perfetto&quot;&gt;Visualizing the trace data using Perfetto&lt;/h2&gt;

&lt;p&gt;Once I had the mechanism in place to generate trace data into a standardized format, the next step was analysis. This is what led me to Perfetto, an open source trace visualization tool that offers an interactive UI to inspect your program’s execution in detail.&lt;/p&gt;

&lt;p&gt;Perfetto is really easy to use. It lets you zoom in on specific timeframes, analyze function call stacks visually, and filter events by category or thread. It even supports SQL queries on your trace data, which opens up a lot of possibilities for custom analysis.&lt;/p&gt;

&lt;p&gt;Here’s a look at the performance visualization for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/blog/img/in-post/perfetto1.png&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/perfetto1.png&quot; alt=&quot;Perfetto Image&quot; title=&quot;Perfetto Image&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Perfetto, you can visualize your program’s entire call flow and easily track memory spikes.&lt;/p&gt;

&lt;h2 id=&quot;how-to-leverage-the-tracing-in-zopen-tools&quot;&gt;How to leverage the tracing in zopen tools&lt;/h2&gt;

&lt;h3 id=&quot;using-zopen-build-instrument-for-easy-tracing&quot;&gt;Using zopen build –instrument for Easy Tracing&lt;/h3&gt;

&lt;p&gt;While this was great, I now wanted a way to make it easy for everyone contributing to zopen to leverage this tracing capability.&lt;/p&gt;

&lt;p&gt;So, I incorporated this functionality into the zopen build framework by adding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--instrument&lt;/code&gt; option:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen build &lt;span class=&quot;nt&quot;&gt;--instrument&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With this option, the open source project is built with instrumentation enabled (applies only to C/C++ projects).&lt;/p&gt;

&lt;p&gt;Then, when the instrumented application runs, it generates a &lt;application&gt;-&lt;timestamp&gt;.json.gz file that contains the trace data. Note, you can override this location and filename by setting the ZOSLIB_PROF_PATH environment variable. You may want to do this in if space is an issue on your system.&lt;/timestamp&gt;&lt;/application&gt;&lt;/p&gt;

&lt;h2 id=&quot;viewing-trace-data&quot;&gt;Viewing Trace Data&lt;/h2&gt;

&lt;p&gt;Now that you have a trace file, the next step is to perform the analysis.&lt;/p&gt;

&lt;p&gt;To view the trace data, there are two approaches:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Manual inspection of the json&lt;/li&gt;
  &lt;li&gt;Using perfetto (preferred)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;manual-inspection&quot;&gt;Manual inspection:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Using zcat and jq: You can decompress and inspect the JSON trace data directly from the command line:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zcat &amp;lt;application&amp;gt;-&amp;lt;timestamp&amp;gt;.json.gz | jq &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;using-perfetto-ui&quot;&gt;Using Perfetto UI&lt;/h3&gt;

&lt;p&gt;The json file can be loaded into the Perfetto UI for a detailed view of the execution.&lt;/p&gt;

&lt;h4 id=&quot;steps-to-view-traces-in-perfetto-ui&quot;&gt;Steps to view traces in Perfetto UI:&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;Visit the &lt;a href=&quot;https://ui.perfetto.dev/&quot;&gt;Perfetto UI&lt;/a&gt; using a browser.&lt;/li&gt;
  &lt;li&gt;Download the generated json.gz file from z/OS and then click on ‘Open Trace file’ in the Perfetto UI.&lt;/li&gt;
  &lt;li&gt;Explore the timeline to see detailed function calls, filter by specific processes or threads, and identify bottlenecks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;W/A/S/D&lt;/code&gt; keys to maneuver around.&lt;/p&gt;

&lt;p&gt;Using Perfetto, I could now visually identify spikes in memory footprint and identify the culprit functions:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/blog/img/in-post/perfetto2.png&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/perfetto2.png&quot; alt=&quot;Perfetto Image&quot; title=&quot;Perfetto Image&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;using-the-sql-query-engine-for-custom-analysis&quot;&gt;Using the SQL Query engine for custom analysis&lt;/h4&gt;

&lt;p&gt;Perfetto also offers a Python library, which I haven’t had the chance to explore yet, but it opens up the possibility for custom analysis and even integrating with large language models (LLMs) for deeper insights.&lt;/p&gt;

&lt;p&gt;Additionally, Perfetto’s built-in SQL query engine allows you to easily locate long-running tasks and perform advanced data queries. Here’s a simple example to find the longest-running functions:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dur&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dur&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This makes it easy to dig into specific performance bottlenecks and get a more granular view of your application’s behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/blog/img/in-post/perfetto3.png&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/perfetto3.png&quot; alt=&quot;Perfetto Image&quot; title=&quot;Perfetto Image&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;This project started as an experiment but has proven to be valuable (for me at least) in quickly detecting performance and memory issues in zopen tools. Moving forward, I’m excited to explore how we can further improve this approach and track additional metrics that could enhance our analysis and optimization efforts.&lt;/p&gt;

&lt;p&gt;If there’s enough interest, I hope to present this in the next zopen guild meeting!&lt;/p&gt;

&lt;p&gt;And of course, there’s always more to do - like figuring out why Git is allocating 80KB of memory!
&lt;a href=&quot;/blog/img/in-post/perfetto.gif&quot; class=&quot;fancybox&quot; data-fancybox=&quot;&quot;&gt;
  &lt;img src=&quot;/blog/img/in-post/perfetto.gif&quot; alt=&quot;Perfetto Image&quot; title=&quot;Perfetto Image&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Thank you to Mike Fulton, Haritha D and everyone from the zopen community, and especially to the IBM Open XL C/C++ team for providing an awesome C/C++ compiler for z/OS!&lt;/p&gt;
</description>
        <pubDate>Thu, 19 Sep 2024 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2024/09/19/tracing/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2024/09/19/tracing/</guid>
        
        <category>Clang</category>
        
        <category>Profiling</category>
        
        <category>Tracing</category>
        
        <category>Debuggin</category>
        
        <category>z/OS</category>
        
        
      </item>
    
      <item>
        <title>Enhancing Vim on z/OS UNIX with Language Server Protocol (LSP) support</title>
        <description>&lt;p&gt;As a long-time Vim user who develops directly on z/OS UNIX (yes, call me old school!), one feature that I’ve always wanted is the support for &lt;strong&gt;Language Server Protocols&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you’re not aware of Language Servers and the Language Server Protocol (LSP), it’s a protocol that standardizes the communication between editors (VS Code, Neovim, Emacs, VIM) and language servers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://microsoft.github.io/language-server-protocol/&quot;&gt;Microsoft’s LSP website&lt;/a&gt; summarizes it well:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The Language Server Protocol (LSP) defines the protocol used between an editor or IDE and a language server that provides language features like auto complete, go to definition, find all references etc. The goal of the Language Server Index Format (LSIF, pronounced like “else if”) is to support rich code navigation in development tools or a Web UI without needing a local copy of the source code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Language servers use JSON-RPC to communicate between the development tool and the language server. Using JSON-RPC allows the server to be implemented in any language.
The protocol itself defines a set of features that can be supported by the server, such as code completion and go-to-definition.&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/vimgo.gif&quot; alt=&quot;tmux.cpp&quot; style=&quot;float:center;&quot; /&gt;
Vim on z/OS with the Go Language Server 
&lt;/p&gt;

&lt;p&gt;For more information on the LSP architecture, visit Microsoft’s &lt;a href=&quot;https://microsoft.github.io/language-server-protocol/overviews/lsp/overview/&quot;&gt;docs&lt;/a&gt;. To find out what languages implement language servers, check out this &lt;a href=&quot;https://microsoft.github.io/language-server-protocol/implementors/servers/&quot;&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;why-lsp-support-is-great-for-editors&quot;&gt;Why LSP support is great for editors&lt;/h2&gt;

&lt;p&gt;LSP’s enable code editors to support the following features without having to write their own Langauge parsers and analyzers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Code Completion&lt;/strong&gt;: Auto-complete suggestions can signifiantly speed up coding because you no longer need to look up the APIs by browsing the manual or system interfaces.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Error Diagnostics&lt;/strong&gt;: This feature helps you maintain clean code as you are actively writing new code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Code Navigation&lt;/strong&gt;: LSPs understand your code and eanbles you to easily navigate to code definitions and call sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ideally, to get the best support for LSPs in a terminal editor, we would want to port Neovim to z/OS. Unfortuantely, the neovim port is still in progress!&lt;/p&gt;

&lt;p&gt;So in the meantime, I’m going to describe how we can provide LSP support to Vim on z/OS.&lt;/p&gt;

&lt;h2 id=&quot;adding-lsp-support-to-vim&quot;&gt;Adding LSP Support to Vim&lt;/h2&gt;

&lt;h3 id=&quot;1-installing-vim&quot;&gt;1. Installing Vim&lt;/h3&gt;

&lt;p&gt;Begin by installing or updating Vim on your system using the &lt;a href=&quot;https://github.com/zopencommunity/meta&quot;&gt;zopen package manager&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;vim
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you’ll need the latest Vim release which includes terminal support (required by the plugins we’ll install below).&lt;/p&gt;

&lt;h3 id=&quot;2-installing-the-vim-plug-plugin-manager&quot;&gt;2. Installing the vim-plug plugin manager&lt;/h3&gt;

&lt;p&gt;Before we set up the LSP support, we’ll need to install a vim plugin manager. As of this writing, &lt;a href=&quot;https://github.com/junegunn/vim-plug&quot;&gt;vim-plug&lt;/a&gt; appears to be the most popular. Install it with the following commands:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# You will need curl for this operation (you can install it via zopen as above)&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-fLo&lt;/span&gt; ~/.vim/autoload/plug.vim &lt;span class=&quot;nt&quot;&gt;--create-dirs&lt;/span&gt; https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.vimrc&lt;/code&gt; file (if it doesn’t exist), and add these lines to enable vim-plug:&lt;/p&gt;

&lt;div class=&quot;language-vim highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;call&lt;/span&gt; plug#begin&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&quot; Add plugins here&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;call&lt;/span&gt; plug#end&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the next step, we’ll install the LSP and auto-complete plugins through vim-plug.&lt;/p&gt;

&lt;h3 id=&quot;3-installing-lsp-and-autocompletion-plugins&quot;&gt;3. Installing LSP and Autocompletion Plugins&lt;/h3&gt;

&lt;p&gt;We’ll now use vim-plug to install the following plugins:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;vim-lsp/vim-lsp-settings&lt;/strong&gt;: These plugins simplify LSP configuration in Vim.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;asyncomplete.vim/asyncomplete-lsp&lt;/strong&gt;: These plugins provide the core functionality for autocompletion in Vim.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Update your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.vimrc&lt;/code&gt; file with the following changes:&lt;/p&gt;

&lt;div class=&quot;language-vim highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;call&lt;/span&gt; plug#begin&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
Plug &lt;span class=&quot;s1&quot;&gt;&apos;prabirshrestha/vim-lsp&apos;&lt;/span&gt;
Plug &lt;span class=&quot;s1&quot;&gt;&apos;IgorTodorovskiIBM/vim-lsp-settings&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;branch&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;zos&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

Plug &lt;span class=&quot;s1&quot;&gt;&apos;prabirshrestha/asyncomplete.vim&apos;&lt;/span&gt;
Plug &lt;span class=&quot;s1&quot;&gt;&apos;prabirshrestha/asyncomplete-lsp.vim&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;call&lt;/span&gt; plug#end&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We’re using my fork of vim-lsp-settings since some of the LSP settings provided in the official repo do not currently work.&lt;/p&gt;

&lt;p&gt;Restart Vim and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:PlugInstall&lt;/code&gt; to install plugins.&lt;/p&gt;

&lt;h3 id=&quot;4-configuring-lsp-for-python&quot;&gt;4. Configuring LSP for Python&lt;/h3&gt;

&lt;p&gt;Now let’s explore how to set up an LSP for Python.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The Python LSP requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clang&lt;/code&gt; in the PATH.&lt;/p&gt;

&lt;p&gt;First, open a Python file with Vim. vim-lsp-settings associates LSPs with the language extension, so make sure the extension is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.py&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;vim hello.py
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use the following command to install the Python language server (pyls).&lt;/p&gt;

&lt;div class=&quot;language-vim highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;LspInstallServer
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After installing, restart vim or reload the file. You should now have auto-complete support via the Python LSP:&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/python_vim.gif&quot; alt=&quot;python vim&quot; style=&quot;float:center;&quot; /&gt;
&lt;/p&gt;

&lt;h3 id=&quot;5-configuring-lsp-for-go&quot;&gt;5. Configuring LSP for Go&lt;/h3&gt;

&lt;p&gt;Now let’s explore how to set up the language server for Go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The Go language server requires the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt; compiler in the PATH. Also make sure to disable CGO with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export CGO_ENABLED=0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, open a Go file with Vim. vim-lsp-settings associates LSPs with the language extension, so make sure the extension is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.go&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;vim main.go
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use the following command to install the Go language server (gopls).&lt;/p&gt;

&lt;div class=&quot;language-vim highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;LspInstallServer
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After installing, restart vim or reload the file. You should now have auto-complete support via the Go language server.&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/vimgo.gif&quot; alt=&quot;python vim&quot; style=&quot;float:center;&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next Steps&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Test out more languages!&lt;/li&gt;
  &lt;li&gt;Neovim! Port LuaJit so that we get all of the Neovim features enabled for z/OS!&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;thank-you&quot;&gt;Thank you&lt;/h1&gt;
&lt;p&gt;Thanks for reading and thanks to Mike Fulton, Haritha D, Peter Haumer and the zopen community contributors!&lt;/p&gt;
</description>
        <pubDate>Thu, 18 Apr 2024 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2024/04/18/vim-autocomplete/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2024/04/18/vim-autocomplete/</guid>
        
        <category>z/OS</category>
        
        <category>Porting</category>
        
        <category>vim</category>
        
        <category>Language Server Protocol</category>
        
        <category>Autocomplete</category>
        
        
      </item>
    
      <item>
        <title>Porting tmux to z/OS</title>
        <description>&lt;p&gt;I’m excited to share my journey of porting &lt;a href=&quot;https://github.com/tmux/tmux/wiki&quot;&gt;tmux&lt;/a&gt;, a popular terminal multiplexer, to z/OS! This project builds upon my previous experience successfully porting GNU screen and making it available under the &lt;a href=&quot;https://github.com/zopencommunity&quot;&gt;z/OS Open Tools&lt;/a&gt; umbrella. Following the success of GNU screen, the z/OS community expressed a strong desire for tmux in a &lt;a href=&quot;https://github.com/orgs/ZOSOpenTools/discussions/433&quot;&gt;recent poll&lt;/a&gt;, making it one of the most requested tools for z/OS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So what is Tmux?&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;tmux is a terminal multiplexer. It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/tmux.gif&quot; alt=&quot;tmux.cpp&quot; style=&quot;float:center;&quot; /&gt;
Tmux running on z/OS
&lt;/p&gt;

&lt;p&gt;Tmux has many advantages over GNU Screen, including mouse integration, supporting terminal splitting into both vertical and horizontal panes, and generally many more configuration options.&lt;/p&gt;

&lt;h2 id=&quot;why-tmux-on-zos&quot;&gt;Why Tmux on z/OS?&lt;/h2&gt;

&lt;p&gt;For z/OS users, particularly system administrators and developers, efficient management of multiple shell sessions is critical for efficiency.&lt;/p&gt;

&lt;p&gt;Tmux provides the following features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Multi-pane Workspaces:&lt;/strong&gt; Organize your terminal by splitting the window into panes, each running an independent program. This is great when you’re building and editing at the same time like me!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Detachable Sessions:&lt;/strong&gt; Leave your work running on z/OS even after disconnecting. Reattach seamlessly from any terminal later. This means you can finish working, sleep and then resume where you left off the following morning.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Enhanced Workflow:&lt;/strong&gt; Easily switch between tasks, monitor multiple processes concurrently, and maintain a clutter-free terminal environment. This is one vital for me since I sometimes work on 5+ different tasks during the day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why not run tmux on Linux? You can certainly run tmux remotely from a Linux machine to manage your screen sessions, but being able to run it locally on z/OS can reduce latency and is especially good for air-gapped environments.&lt;/p&gt;

&lt;h2 id=&quot;how-do-i-install-it&quot;&gt;How do I install it?&lt;/h2&gt;

&lt;p&gt;Grab it from z/OS Open Tools using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; package manager:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen install tmux -y
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then run it:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;tmux
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To learn more about the shortcuts, I recommend you read this &lt;a href=&quot;https://tmuxcheatsheet.com/&quot;&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;porting-tmux-to-zos&quot;&gt;Porting Tmux to z/OS&lt;/h2&gt;

&lt;p&gt;Porting tmux to z/OS presented unique challenges due to the underlying differences between z/OS and traditional Unix-like systems. Let’s explore some key areas and the solutions implemented:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Missing dependencies&lt;/strong&gt;: Tmux in itself does not have that many library dependencies. It depends on libevent and ncurses. Fortunately ncurses has already been ported for other projects like Vim and Less so I only had to focus on getting libevent ported.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;I/O Handling:&lt;/strong&gt; Standard Unix I/O operations might require special handling on z/OS. We leveraged functionalities provided by ZOSLIB (introduced in the &lt;a href=&quot;https://igortodorovskiibm.github.io/blog/&quot;&gt;previous blog post&lt;/a&gt;) to simplify I/O handling.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conquering-dependency-challenges&quot;&gt;Conquering Dependency Challenges:&lt;/h2&gt;

&lt;p&gt;By leveraging the &lt;a href=&quot;https://zopencommunity.github.io/meta/#/Guides/Porting&quot;&gt;zopen build framework&lt;/a&gt;, I was able to port libevent fairly easily. The port is available at &lt;a href=&quot;https://github.com/ZOSOpenTools/libeventport&quot;&gt;https://github.com/zopencommunity/libeventport&lt;/a&gt;. Since libevent is a library, I had to make sure I exposed it as a library when I was adding it as a dependency for Tmux with the following additions to its &lt;a href=&quot;https://github.com/ZOSOpenTools/libeventport/blob/main/buildenv#L16-L26&quot;&gt;buildenv&lt;/a&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  export ZOPEN_EXTRA_CFLAGS=&quot;\${ZOPEN_EXTRA_CFLAGS} -I\$PWD/include&quot;
  export ZOPEN_EXTRA_CXXFLAGS=&quot;\${ZOPEN_EXTRA_CXXFLAGS} -I\$PWD/include&quot;
  export ZOPEN_EXTRA_LDFLAGS=&quot;\${ZOPEN_EXTRA_LDFLAGS} -L\$PWD/lib&quot;
  export ZOPEN_EXTRA_LIBS=&quot;\${ZOPEN_EXTRA_LIBS} -levent&quot;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a detailed look on how I built it, check out the entire buildenv configuration here &lt;a href=&quot;https://github.com/ZOSOpenTools/libeventport/blob/main/buildenv&quot;&gt;https://github.com/zopencommunity/libeventport/blob/main/buildenv&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now I was ready to build Tmux.&lt;/p&gt;

&lt;h2 id=&quot;building-tmux&quot;&gt;Building tmux&lt;/h2&gt;

&lt;p&gt;tmux’s buildenv configuration was relatively &lt;a href=&quot;https://github.com/zopencommunity/tmuxport/blob/main/buildenv&quot;&gt;simple&lt;/a&gt;. We added the known dependencies (libevent, ncurses, make, and a few others) and chose the Clang compiler for building. To build it, I used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen build&lt;/code&gt; framework.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen build -v
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, tmux had a number of issues at build and runtime and I’ll go through a few of them.&lt;/p&gt;

&lt;h2 id=&quot;overcoming-the-forkpty-hurdle&quot;&gt;Overcoming the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;forkpty&lt;/code&gt; hurdle&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;forkpty&lt;/code&gt; is needed by tmux but is absent on z/OS, so I employed an alternative approach utilizing existing C LE apis. This involved creating a pseudo terminal by opening specific devices and manipulating terminal attributes to simulate a terminal environment for tmux sessions. You can find the patch &lt;a href=&quot;https://github.com/zopencommunity/tmuxport/blob/main/patches/forkpty-zos.c.patch&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;testing-and-debugging-the-build&quot;&gt;Testing and Debugging the Build&lt;/h2&gt;

&lt;p&gt;After testing the build of tmux, I was presented with garbled output immediately! To understand the issue, I leveraged tmux’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; option for logging. After further investigation, I realized that the content written to the screen was actually readable and in ASCII format but it wasn’t being converted back to IBM-1047 for the terminal.&lt;/p&gt;

&lt;p&gt;The culprit? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;writev&lt;/code&gt; was not respecting file tags and auto-conversion. This meant the text going to standard output wasn’t being automatically converted to the terminal’s CCSID (IBM-1047).&lt;/p&gt;

&lt;p&gt;To resolve this, I modified the libevent code to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;writev&lt;/code&gt;. This meant I had to create a patch for libevent &lt;a href=&quot;https://github.com/zopencommunity/libeventport/blob/main/patches/buffer.c.patch&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This enabled tmux to render text correctly, but I wasn’t done yet. The newline characters didn’t seem to register. This resulted in the prompt line being printed without a newline!&lt;/p&gt;

&lt;p&gt;After comparing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stty&lt;/code&gt; output from a working terminal to that of the tmux created terminal, I noticed that the flags OPOST and ONLCR were missing. Adding them &lt;a href=&quot;https://github.com/zopencommunity/tmuxport/blob/main/patches/spawn.c.patch#L11&quot;&gt;back in the tmux C code&lt;/a&gt; fixed it!&lt;/p&gt;

&lt;p&gt;The last major issue was with regards to socketpair. Tmux uses socketpairs instead of pipes for interprocess communication, but socketpairs (as of z/OS 2.4) do not support auto-conversion. Changing the code to &lt;a href=&quot;https://github.com/zopencommunity/tmuxport/blob/2dc478690641fa88a4596c64c873fae0c20ad2e0/patches/job.c.patch#L10&quot;&gt;use pipes instead of socketpairs&lt;/a&gt; fixed this issue!&lt;/p&gt;

&lt;p&gt;There’s still a lingering issue when using /bin/sh as the default shell. So for now I set my tmux default shell to bash to overcome it in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME/.tmux.conf&lt;/code&gt; tmux config file as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;vim $HOME/.tmux.conf
# Now add the following line
set-option -g default-shell $ZOPEN_PKGINSTALL/bash/bash/bin/bash
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For a more involved tmux.config file, you can check mine out &lt;a href=&quot;https://github.com/IgorTodorovskiIBM/tmux.conf/blob/main/.tmux.conf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h1&gt;
&lt;p&gt;There’s still a few issues remaining, but for the most part tmux is pretty much functional. Here’s what remains:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Resolve issues when using /bin/sh as the default shell&lt;/li&gt;
  &lt;li&gt;Migrating the forkpty and writev changes to the common zoslib library&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;thank-you&quot;&gt;Thank you&lt;/h1&gt;
&lt;p&gt;Thanks for reading and thanks to Mike Fulton, Haritha D and the z/OS Open Tools contributors!&lt;/p&gt;
</description>
        <pubDate>Tue, 12 Mar 2024 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2024/03/12/tmux/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2024/03/12/tmux/</guid>
        
        <category>z/OS</category>
        
        <category>Porting</category>
        
        <category>tmux</category>
        
        <category>Terminal Multiplexer</category>
        
        
      </item>
    
      <item>
        <title>Exploring ZOSLIB: An open source z/OS library for porting</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Ever tried porting your favorite Linux tool to z/OS? As we’ll see, it’s not as simple as grabbing the source and building it on z/OS!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My role at the &lt;a href=&quot;https://github.com/zopencommunity&quot;&gt;zopen community&lt;/a&gt; involves the task of porting open-source tools to the &lt;a href=&quot;https://www.ibm.com/products/zos&quot;&gt;IBM z/OS operating system&lt;/a&gt;. This requires navigating through the various differences between Linux and z/OS, such as:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;C runtime differences (&lt;a href=&quot;https://www.gnu.org/software/libc/&quot;&gt;GNU C Library (Glibc)&lt;/a&gt; vs &lt;a href=&quot;https://www.ibm.com/docs/en/zos/3.1.0?topic=cc-zos-runtime-library-reference&quot;&gt;z/OS C Runtime Library&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;File system differences (z/OS UNIX/datasets on z/OS)&lt;/li&gt;
  &lt;li&gt;Endianness differences&lt;/li&gt;
  &lt;li&gt;Security differences (RACF/SAF on z/OS)&lt;/li&gt;
  &lt;li&gt;Codepages differences (EBCDIC/ASCII/UTF8 program support in z/OS).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One significant hurdle that we continue to encounter is the &lt;strong&gt;absence of essential C runtime APIs on z/OS&lt;/strong&gt;. While we can implement workarounds at the application source-code level, it doesn’t seem like a sustainable model. Can we do better?&lt;/p&gt;

&lt;p&gt;Moreover, adding to the complexity of porting to z/OS, z/OS UNIX supports &lt;a href=&quot;https://www.ibm.com/docs/en/zos/3.1.0?topic=ascii-file-tagging-in-enhanced&quot;&gt;file tag metadata&lt;/a&gt; that describe a file’s encoding content. File tag metadata doesn’t exist in Linux, but it’s crucial in z/OS because programs can run in various character modes, including EBCDIC/ASCII/UTF8 mode. What makes file tags interesting is that, combined with the z/OS Enhanced ASCII services, z/OS can perform automatic conversion of files to and from the program’s encoding upon read and write. This enables EBCDIC/ASCII/UTF8 programs to interpret file data correctly, irregardless of the program’s character mode. This feature greatly simplifies handling files of various codepages in applications. However, although file tags are great, many z/OS services generate files with no file tag metadata (these are known as “untagged files”). How are applications supposed to handle such “untagged” files?&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;ZOSLIB&lt;/strong&gt; jumps in — it addresses many of these complexities and makes porting to z/OS a little bit simpler!&lt;/p&gt;

&lt;h2 id=&quot;what-is-zoslib&quot;&gt;What is ZOSLIB?&lt;/h2&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/zoslib.png&quot; alt=&quot;ZOSLIB&quot; style=&quot;float:center;&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;At its core, &lt;a href=&quot;https://github.com/ibmruntimes/zoslib&quot;&gt;ZOSLIB&lt;/a&gt; is an open source extension of the z/OS LE C Runtime Library, offering the following additional functionalities:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Extended POSIX Support&lt;/strong&gt;: ZOSLIB includes a subset of POSIX APIs not present in the LE C Runtime Library, addressing critical functionality gaps.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Enhanced File Operations&lt;/strong&gt;: Improved implementations for C open, pipe, and more which facilitate seamless integration with z/OS auto-conversion facilities.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Data Conversion Utilities&lt;/strong&gt;: C APIs for EBCDIC to ASCII conversions and vice versa simplify the handling of different character encodings.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Diagnostic Reporting&lt;/strong&gt;: ZOSLIB provides APIs for enhanced diagnostic reporting, aiding in troubleshooting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ZOSLIB was originally introduced as a library to aid in porting Node.js and its components to z/OS. It has since been open-sourced and is now used by over 100 open-source projects under the zopen community organization.&lt;/p&gt;

&lt;p&gt;The ZOSLIB project is available on GitHub at &lt;a href=&quot;https://github.com/ibmruntimes/zoslib&quot;&gt;https://github.com/ibmruntimes/zoslib&lt;/a&gt;. It is licensed under the Apache 2.0 license, which imposes few restrictions and facilitates modification and redistribution.&lt;/p&gt;

&lt;p&gt;ZOSLIB is a 64-bit ASCII library and is meant to be leveraged by ASCII-based applications and libraries.&lt;/p&gt;

&lt;p&gt;Now, let’s explore the gaps that ZOSLIB aims to fill.&lt;/p&gt;

&lt;h2 id=&quot;gap-1-how-do-we-handle-untagged-files&quot;&gt;Gap 1: How do we handle untagged files?&lt;/h2&gt;

&lt;p&gt;File tags are very useful metadata for determining the underlying codepage of a file. However, z/OS does not make this metadata field mandatory. z/OS UNIX files can lack a tag, and such files are considered untagged. Complicating matters further is that by default, C LE &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;creat()&lt;/code&gt; functions generate untagged files even when the program is running in ASCII mode. Additional code is required to tag newly created files.&lt;/p&gt;

&lt;p&gt;So why are untagged files an issue? They’re a problem because the application can not determine the true encoding of untagged files. This means that an EBCDIC-based program expecting text data will interpret the data one way and an ASCII-based program will interpret the data another way. Many applications address this by introducing an option or environment variable to toggle how untagged files are interpreted.&lt;/p&gt;

&lt;p&gt;To address this issue, in zoslib, we implemented an override for the C &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open()&lt;/code&gt; function that incorporates an &lt;strong&gt;optimized heuristic&lt;/strong&gt; (toggleable if needed by an environment variable), that determines the true encoding of untagged files. Additionally, since z/OS creates untagged files (and pipes) by default, and there is no toggle to control this behavior, we decided to override C &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open()&lt;/code&gt;, ‘pipe()’, and ‘creat()’ to additionally tag all files/pipes to ASCII ISO8859-1 by default. This override can be adjusted via an environment variable (documented in the zoslib manpages).&lt;/p&gt;

&lt;p&gt;When zoslib is added to your project and the macro -DZOSLIB_OVERRIDE_CLIB=1 is set, all references to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open()&lt;/code&gt;, ‘pipe()’, and ‘creat()’ are mapped to a new zoslib function. In the case of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open&lt;/code&gt;, it’s mapped to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__open_ascii&lt;/code&gt; function (defined &lt;a href=&quot;https://github.com/ibmruntimes/zoslib/blob/3a2b4b06aa52095be6c805f921b101e9ff8c9a15/src/zos-io.cc#L808&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This enhancement has enabled zoslib applications to seamlessly work with both legacy untagged data and tagged data. For our Git to z/OS port, this entire support reduced the number of changes by close to 50%!&lt;/p&gt;

&lt;h2 id=&quot;gap-2-missing-c-runtime-apis&quot;&gt;Gap 2: Missing C Runtime APIs&lt;/h2&gt;

&lt;p&gt;The ZOSLIB project also enables developers to implement missing C runtime functions once and then make them available to all projects that leveraging zoslib. For instance, several open source projects have transitioned to using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posix_spawn&lt;/code&gt; rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fork&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt;. Rather than implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posix_spawn&lt;/code&gt; for each individual application, we implemented it once in zoslib and leverage it across many projects. Additional functions added to zoslib include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdtemp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execvpe&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strpcpy&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strsignal&lt;/code&gt; and many more.&lt;/p&gt;

&lt;p&gt;Additionally, there are instances where a header file may be missing. For example, on z/OS, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sys/param.h&lt;/code&gt; does not exist, and the macro definitions that are typically in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sys/param.h&lt;/code&gt; are present in other headers. However, the assumption by open source projects is that this header exists across all operating systems. Without guarding out the header include via a #ifdef check, the compiler would trigger an error. However, this means making an application specific change. By adding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sys/param.h&lt;/code&gt; header to zoslib, we avoid yet another change to the application’s source code.&lt;/p&gt;

&lt;p&gt;The good news is that many of these APIs are currently being implemented in the z/OS LE C runtime library, but zoslib enables us to adopt an agile approach to development, allowing us to promptly address these gaps as we encounter them.&lt;/p&gt;

&lt;h2 id=&quot;gap-3-provide-system-level-interfaces-for-zos&quot;&gt;Gap 3: Provide System-Level Interfaces for z/OS&lt;/h2&gt;

&lt;p&gt;Operating systems like Linux also provide system-level functions, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sysconf(_SC_NPROCESSORS_ONLN);&lt;/code&gt;, which retrieves the number of online CPUs for the system. Such system-level functions vary from platform to platform, and offering interfaces to capture equivalent implmentations aids in porting.&lt;/p&gt;

&lt;p&gt;For instance, the following functions have been added to zoslib:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__get_num_online_cpus&lt;/code&gt; - capture the number of online cpus&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__get_os_level&lt;/code&gt; - return the operating system level&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__get_cpu_model&lt;/code&gt; - returns the cpu model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are just a few examples of what ZOSLIB has to offer and I encourage to read the source and examine the zoslib documentation for more information.&lt;/p&gt;

&lt;h2 id=&quot;how-were-porting-tools-with-zoslib&quot;&gt;How we’re porting tools with ZOSLIB&lt;/h2&gt;

&lt;p&gt;Now that we’ve examined what ZOSLIB has to offer, let’s explore how you can seamlessly integrate ZOSLIB into your application or library.&lt;/p&gt;

&lt;p&gt;To leverage ZOSLIB, you can either compile it from source as described in the &lt;a href=&quot;https://github.com/ibmruntimes/zoslib&quot;&gt;README&lt;/a&gt; or you can download a release from the zopen &lt;a href=&quot;https://github.com/zopencommunity/zoslibport/releases&quot;&gt;zoslibport repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This release provides the libraries (static and dynamic), header files, man pages, as well as environment variables for use in the zopen framework.&lt;/p&gt;

&lt;p&gt;We’ll first cover how to integrate zoslib into your code manually, and then describe how we’re automatically integrating zoslib into the 100+ zopen community projects.&lt;/p&gt;

&lt;h2 id=&quot;integrating-zoslib-manually&quot;&gt;Integrating zoslib manually&lt;/h2&gt;

&lt;p&gt;First, download a stable release of zoslib &lt;a href=&quot;https://github.com/zopencommunity/zoslibport/releases&quot;&gt;here&lt;/a&gt;. If you have the zopen package manager, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt;, on your system (details &lt;a href=&quot;https://zopencommunity.github.io/meta/#/Guides/QuickStart&quot;&gt;here&lt;/a&gt;), you can simply run:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen install zoslib
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can download the pax.Z asset and extract it onto your system.&lt;/p&gt;

&lt;p&gt;Let’s assume you have a simple hello world program that writes a file to disk.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;//a.c
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {
    const char *filename = &quot;output.txt&quot;;
    const char *message = &quot;Hello, World!\n&quot;;

    int file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);

    if (file == -1) {
        perror(&quot;Error opening file&quot;);
        return 1; 
    }

    ssize_t bytes_written = write(file, message, strlen(message));

    if (bytes_written == -1) {
        perror(&quot;Error writing to file&quot;);
        close(file);
        return 1; 
    }

    close(file);

    return 0; 
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The below example uses the clang compiler, available &lt;a href=&quot;https://epwt-www.mybluemix.net/software/support/trial/cst/programwebsite.wss?siteId=1803&quot;&gt;here&lt;/a&gt;. If you do not have clang on your z/OS system, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xlc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xlclang&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s compile this C program using clang as follows:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;clang -fzos-le-char-mode=ascii -m64 -o a.out a.c
./a.out # run it
ls -lT output.txt # check the tag
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;- untagged    T=off -rw-rw-r--   1 ITODORO  CDEV          14 Feb 12 15:09 output.txt
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will notice that the output.txt file is untagged.&lt;/p&gt;

&lt;p&gt;Now let’s incorporate zoslib. Set ZOSLIB_HOME to the path to your zoslib installation.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;clang -fzos-le-char-mode=ascii -m64 -DZOSLIB_OVERRIDE_CLIB=1 -isystem $ZOSLIB_HOME/include -L $ZOSLIB_HOME/lib -o a.out a.c -lzoslib $ZOSLIB_HOME/lib/celquopt.s.o -lzoslib-supp
./a.out
ls -lT output.txt
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;t ISO8859-1   T=on  -rw-rw-r--   1 ITODORO  CDEV          14 Feb 12 14:52 output.txt
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the output.txt file is now tagged as ISO8859-1. This is because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open()&lt;/code&gt; is now overriden with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__open_ascii()&lt;/code&gt; from zoslib as instructed by the macro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-DZOSLIB_OVERRIDE_CLIB=1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s examine a simpler way of integrating ZOSLIB, and that is by leveraging the zopen framework. This is how zoslib is currently being leveraged in over 100+ zopen projects.&lt;/p&gt;

&lt;h2 id=&quot;leveraging-the-zopen-build-framework&quot;&gt;Leveraging the zopen build framework&lt;/h2&gt;

&lt;p&gt;Examine the following code snippet:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;export ZOPEN_BUILD_LINE=&quot;STABLE&quot;
export ZOPEN_STABLE_URL=&quot;http://www.greenwoodsoftware.com/less/less-${LESS_VERSION}.tar.gz&quot;
export ZOPEN_STABLE_DEPS=&quot;make curl gzip tar ncurses zoslib&quot;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The above snippet, taken from &lt;a href=&quot;https://github.com/zopencommunity/lessport/blob/main/buildenv&quot;&gt;lessport&lt;/a&gt; describes how simple it is to leverage zoslib using the zopen build framework.
Simply adding zoslib as a dependency (in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZOPEN_STABLE_DEPS&lt;/code&gt;) will instruct the build to add zoslib as a dependency and pass in the zoslib compiler flags.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: The great news is that zoslib is now always added as a dependency. As such, you no longer need to add zoslib as a dependency in ZOPEN_STABLE_DEPS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These flags automatically instruct the compiler to pick up the necessary header files and to link to the ZOSLIB static archives. If you’re wondering, these flags are passed in via the .appenv file from your ZOSLIB_HOME installation. The file is generated by the &lt;a href=&quot;https://github.com/zopencommunity/zoslibport/blob/4bf40195e8c646d34b3fdc6299c0f00a3f9442f1/buildenv#L45&quot;&gt;zoslibport build configuration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zopen&lt;/code&gt; build framework sets the CPPFLAGS, CXXFLAGS, CFLAGS, LIBS, LDFLAGS accordingly. Fortunately, almost every projects we deal with rely on these flags, meaning that we can easily pass integrate ZOSLIB as an additional library to any port.&lt;/p&gt;

&lt;p&gt;Since late 2022, several z/OS ported projects, including Git, Curl, Bash and many more have been successfully integrated with zoslib. Open Source communities have also adopted our dependency on zoslib, including GNU Make, Bash, Vim and more!&lt;/p&gt;

&lt;p&gt;If you’re interested in porting to zopen and would like to learn how, check out our documentation &lt;a href=&quot;https://zopencommunity.github.io/meta/#/Guides/Porting&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;contributing-to-zoslib&quot;&gt;Contributing to ZOSLIB&lt;/h2&gt;

&lt;p&gt;Contributions to ZOSLIB are encouraged, and the process involves opening pull requests at https://github.com/ibmruntimes/zoslib/pulls.&lt;/p&gt;

&lt;h2 id=&quot;future-considerations-for-zoslib&quot;&gt;Future considerations for ZOSLIB&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Consider creating routines to expose z/OS services like SAF / CMS APIs&lt;/li&gt;
  &lt;li&gt;Add more missing POSIX functions!&lt;/li&gt;
  &lt;li&gt;Support for building on V2R5 and V2R3 targets&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;special-thanks&quot;&gt;Special Thanks&lt;/h1&gt;
&lt;p&gt;Thank you to &lt;a href=&quot;https://github.com/MikeFultonDev&quot;&gt;Mike Fulton&lt;/a&gt;, &lt;a href=&quot;https://github.com/gabylb&quot;&gt;Gaby Baghdadi&lt;/a&gt;, &lt;a href=&quot;https://github.com/zsw007&quot;&gt;Wayne Zhang&lt;/a&gt;, &lt;a href=&quot;https://github.com/ccw-1&quot;&gt;CW Cheung&lt;/a&gt;, &lt;a href=&quot;https://github.com/perry-ca&quot;&gt;Sean Perry&lt;/a&gt;, &lt;a href=&quot;https://github.com/mfsysprog&quot;&gt;Eric Janssen&lt;/a&gt;, &lt;a href=&quot;https://github.com/HarithaIBM&quot;&gt;Haritha D&lt;/a&gt; and many others for their contributions to ZOSLIB.&lt;/p&gt;
</description>
        <pubDate>Mon, 12 Feb 2024 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2024/02/12/zoslib-library/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2024/02/12/zoslib-library/</guid>
        
        <category>zoslib</category>
        
        <category>z/OS</category>
        
        <category>Library</category>
        
        <category>Porting</category>
        
        
      </item>
    
      <item>
        <title>AI on z/OS: LLaMa and LLaMa.cpp</title>
        <description>&lt;p&gt;In the rapidly evolving field of AI, Large Language Models (LLM)’s like &lt;strong&gt;LLaMa&lt;/strong&gt; and the open source inference engine, &lt;strong&gt;LLaMa.cpp&lt;/strong&gt;, are quickly becoming instrumental in bridging the gap between cutting-edge AI models and their practical deployment on common architectures. We’ll describe LLaMa’s significance, uncover the benefits of LLaMa.cpp, and examine their relevance to &lt;strong&gt;z/OS&lt;/strong&gt;, as well as explore the challenges of adapting the LLaMa model and the LLaMa.cpp inference engine to the z/OS platform.&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/ai2.png&quot; alt=&quot;LLaMa.cpp&quot; style=&quot;float:center;&quot; /&gt;
LLaMa.cpp&apos;s server program running on z/OS
&lt;/p&gt;

&lt;h2 id=&quot;the-llama-model&quot;&gt;The LLaMa Model&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://ai.meta.com/llama/&quot;&gt;LLaMa&lt;/a&gt;&lt;/strong&gt;, or Large Language Model Meta AI, was introduced in February 2023 by Meta. The LLaMa set of large language models (LLMs) quickly gained attention for its ability to generate human-like text and comprehend vast language data, with very similar accuracy to ChatGPT.&lt;/p&gt;

&lt;p&gt;Unlike OpenAI’s ChatGPT/GPT-4 models, which have remained closed, Meta opened up its LLaMa v1 model to the community under a noncommercial license.&lt;/p&gt;

&lt;p&gt;Then, in July 2023, Meta unveiled LLaMa 2, introducing models with 7 billion, 13 billion, 33 and 70 billion parameters, and this time made its LLM models available for commercial use as well. Additionally, Llama 2 models were trained with 40% more data, leading to much better results than before.&lt;/p&gt;

&lt;p&gt;Meta also released models that were fine-tuned for chat, including Llama-2-7b-chat and Llama-2-13b-chat, which were fine-tuned using reinforcement learning from human feedback.&lt;/p&gt;

&lt;h3 id=&quot;llama-models-and-performance&quot;&gt;LLaMa Models and Performance&lt;/h3&gt;

&lt;p&gt;The LLaMa models achieved great scores across the board, as evidenced by Hugging Face’s LLM leaderboard. The Hugging Face &lt;a href=&quot;https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard&quot;&gt;leaderboard&lt;/a&gt; measures and tracks the top open LLM models.&lt;/p&gt;

&lt;p&gt;The model with the best trade-off, though, is LLaMa’s 13-billion parameter model, which &lt;a href=&quot;https://arxiv.org/abs/2302.13971&quot;&gt;outperformed&lt;/a&gt; much larger models like GPT-3 (with 175 billion parameters) in various language tasks, but with the benefit of being able to run it on lower cost systems!&lt;/p&gt;

&lt;h2 id=&quot;can-we-run-it-on-a-cpu-based-architecture&quot;&gt;Can we run it on a CPU Based Architecture?&lt;/h2&gt;

&lt;p&gt;Until now, it has been widely assumed that a high-powered GPU is necessary for performing efficient transformations and inferencing. However, LLaMa.cpp has changed the game by enabling CPU-based architectures to run LLM models at a reasonable speed!&lt;/p&gt;

&lt;h3 id=&quot;introducing-llamacpp---a-game-changer-in-ai&quot;&gt;Introducing LLaMa.cpp - A Game Changer in AI&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ggerganov/llama.cpp&quot;&gt;LLaMa.cpp&lt;/a&gt;, an open source LLaMa inference engine, is a new groundbreaking C++ inference engine designed to run LLaMa models efficiently.&lt;/p&gt;

&lt;p&gt;It achieves this through its use of &lt;strong&gt;quantization&lt;/strong&gt;. Quantization is used to reduce the precision of the model weights. This saves memory and makes calculations much faster. For LLaMa models, such as Llama 2-7B, quantization reduces the memory requirements from 13Gb to 4gb and for the LLaMa-13B model, from 24 GB to 7.8gb.&lt;/p&gt;

&lt;p&gt;If you’re interested in learning more about the quantization method used in LLaMa.cpp, the article &lt;a href=&quot;https://finbarr.ca/how-is-llama-cpp-possible/&quot;&gt;How is LLaMa.cpp possible?
&lt;/a&gt; does a great job explaining it.&lt;/p&gt;

&lt;p&gt;Additionally, LLaMa.cpp is written in C++ and provides C headers to its interfaces. This means that you can bind it to your favourite languages, such as Node.js, Go, and Python!&lt;/p&gt;

&lt;h2 id=&quot;performance-across-devices&quot;&gt;Performance Across Devices&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ggerganov/llama.cpp/issues/34&quot;&gt;LLaMa Benchmark reports&lt;/a&gt; reveal significant improvements over the original models, in some cases offering up to 3-4 times speedup from quanitization.&lt;/p&gt;

&lt;p&gt;Their &lt;a href=&quot;https://github.com/ggerganov/llama.cpp&quot;&gt;github page&lt;/a&gt; also reports the following statistics across the various quantization methods:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Model&lt;/th&gt;
      &lt;th&gt;Measure&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;F16&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Q4_0&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Q4_1&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Q5_0&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Q5_1&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Q8_0&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7B&lt;/td&gt;
      &lt;td&gt;perplexity&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.9066&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;6.1565&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;6.0912&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.9862&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.9481&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.9070&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7B&lt;/td&gt;
      &lt;td&gt;file size&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13.0G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;3.5G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;3.9G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;4.3G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;4.7G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;6.7G&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7B&lt;/td&gt;
      &lt;td&gt;ms/tok @ 4th&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;127&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;55&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;54&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;76&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;83&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;72&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7B&lt;/td&gt;
      &lt;td&gt;ms/tok @ 8th&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;122&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;43&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;45&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;52&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;56&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;67&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7B&lt;/td&gt;
      &lt;td&gt;bits/weight&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;16.0&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;4.5&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.0&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.5&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;6.0&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;8.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13B&lt;/td&gt;
      &lt;td&gt;perplexity&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.2543&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.3860&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.3608&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.2856&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.2706&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.2548&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13B&lt;/td&gt;
      &lt;td&gt;file size&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;25.0G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;6.8G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7.6G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;8.3G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;9.1G&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13G&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13B&lt;/td&gt;
      &lt;td&gt;ms/tok @ 4th&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;103&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;105&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;148&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;160&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;131&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13B&lt;/td&gt;
      &lt;td&gt;ms/tok @ 8th&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;73&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;82&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;98&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;105&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;128&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13B&lt;/td&gt;
      &lt;td&gt;bits/weight&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;16.0&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;4.5&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.0&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.5&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;6.0&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;8.5&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As you can see, going from F16 (16-bit float) to Q4_0 quantization gets you a 1.5 times speedup. Also, perplexity which is a common metric for measuring how effective an LLM is, does not suffer greatly from quantization! Awesome!&lt;/p&gt;

&lt;p&gt;The power of LLaMa.cpp has already been evidenced in the field with these examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/rgerganov/status/1635604465603473408&quot;&gt;On a Pixel 5, the 7B model manages 1 token per second&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://simonwillison.net/2023/Mar/11/llama/&quot;&gt;An M2 MacBook Pro achieves around 16 tokens per second with the same model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;llama-meets-zos&quot;&gt;LLaMa Meets z/OS&lt;/h2&gt;

&lt;p&gt;z/OS, the operating system for IBM Z mainframes, serves as the backbone for critical operations across many industries. Data is everything, and z/OS systems process a lot of data. Merging AI with z/OS opens new doors for business and financial intelligence. IBM is already doing a fantastic job at this by introducing the &lt;a href=&quot;https://community.ibm.com/community/user/ibmz-and-linuxone/blogs/evan-rivera/2023/02/24/python-ai-toolkit-for-ibm-zos&quot;&gt;Python AI Toolkit for IBM z/OS&lt;/a&gt; as well as &lt;a href=&quot;https://www.ibm.com/watsonx&quot;&gt;WatsonX&lt;/a&gt;, which also plans to make the LLaMa 2 models available on its platform!&lt;/p&gt;

&lt;p&gt;LLaMa.cpp, is yet another option and it aligns well with z/OS’s CPU-based architecture and large memory cache.&lt;/p&gt;

&lt;p&gt;One other reason for running LLaMa.cpp locally on z/OS is security. Data on z/OS machines is typically sensitive, and as such, many clients choose to air-gap their systems. (Air-gapping isolates a computer or network from external connections). For sensitive industries like finance and healthcare, local AI models are critical for security by limiting exposure to external threats.&lt;/p&gt;

&lt;h2 id=&quot;im-convinced-lets-port-llamacpp-to-zos&quot;&gt;I’m convinced! let’s port LLaMa.cpp to z/OS&lt;/h2&gt;

&lt;p&gt;Let’s now discuss how we can get LLaMa.cpp to work on z/OS.&lt;/p&gt;

&lt;p&gt;Porting LLaMa.cpp to z/OS isn’t without its difficulties. Challenges include:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;C Runtime Adaptation&lt;/strong&gt;: Porting C/C++ code involves dealing with a different C runtime on z/OS, known as the Language Environment (LE). Fortunately, in the case of LLaMa.cpp, we were able to leverage the &lt;a href=&quot;https://github.com/ibmruntimes/zoslib&quot;&gt;zoslib library&lt;/a&gt;, which bridges the gap between the Linux C runtime and the z/OS C runtime. ✅&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Missing Tools&lt;/strong&gt;: LLaMa.cpp has dependencies on common build tools such as GNU Make and CMake. These tools are not natively available on z/OS. However, we were able to leverage the work from &lt;a href=&quot;https://github.com/zopencommunity&quot;&gt;the zopen community&lt;/a&gt; and use these tools to build LLaMa.cpp with no issues. ✅&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Endianness Issues&lt;/strong&gt;: 99% of the open LLM models, including LLaMa’s models, are in little endian format. IBM Z is a big endian platform. Therefore, we need a mechanism to convert models from little endian to big endian. 
Either the models need to be converted as a pre-step prior to inferencing, or the inferencing engine needs to be able to auto convert little endian models to big endian. Let’s examine both approaches in more detail.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a look at the patches applied to get LLaMa.cpp to work, visit &lt;a href=&quot;https://github.com/zopencommunity/llamacppport&quot;&gt;llamacppport&lt;/a&gt;. (Note, this was hacked in a weekend. :) I list the future consideration at the end of this blog).&lt;/p&gt;

&lt;h3 id=&quot;approach-1---converting-models-from-little-endian-to-big-endian&quot;&gt;Approach 1 - Converting models from little endian to big endian&lt;/h3&gt;

&lt;p&gt;LLaMa.cpp requires models to be in its own unique GGJT format (soon to be updated to GGUF), and it provides a handy script, &lt;a href=&quot;https://github.com/ggerganov/llama.cpp/blob/master/convert.py&quot;&gt;convert.py&lt;/a&gt; that can convert any LLaMa model to this format. Fortunately, this provided a way for us to additionally write out the content in big endian format after conversion.&lt;/p&gt;

&lt;p&gt;For example, the conversion script, which was written in Python, writes out 32-bit integer values as follows:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;self.fout.write(struct.pack(&quot;i&quot;, len(text)))
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adding an “!” swapped the bytes so that it would be written out in big endian order.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;self.fout.write(struct.pack(&quot;!i&quot;, len(text)))
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I ran the conversion script on Linux x86, but if we were to run it on IBM Z, we would swap the bytes at read-time, as opposed to write-time.&lt;/p&gt;

&lt;p&gt;However, I wasn’t very happy with this approach because it meant that I couldn’t directly download a model from &lt;a href=&quot;https://huggingface.co/&quot;&gt;huggin face&lt;/a&gt; and have it just work.&lt;/p&gt;

&lt;h3 id=&quot;approach-2---modifing-the-llamacpp-code-to-dynamically-convert-models-to-big-endian&quot;&gt;Approach 2 - Modifing the LLaMa.cpp code to dynamically convert models to big endian&lt;/h3&gt;

&lt;p&gt;When the LLaMa.cpp inference engine starts up, it reads the model into memory. It provides routines to read float and integer data. Therefore, after reading integer or float data, we could swap the bytes to match the native endian format, big endian. This required some knowledge of the tensor weight format, but with a little bit of work, we wre able to get it to work!&lt;/p&gt;

&lt;p&gt;For a closer look at how this was achieved, view the patches &lt;a href=&quot;https://github.com/zopencommunity/llamacppport/blob/main/patches/PR1.patch&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;llama-running-on-zos&quot;&gt;LLaMa running on z/OS!&lt;/h2&gt;

&lt;p&gt;After successfully applying these changes, we are now able to run LLaMa.cpp on z/OS!&lt;/p&gt;

&lt;p&gt;If you’re interested in installing LLaMa.cpp, you can download it using &lt;a href=&quot;https://zosopentools.github.io/meta/#/Guides/QuickStart&quot;&gt;zopen&lt;/a&gt; as follows:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;zopen install llamacpp
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or download the &lt;a href=&quot;https://github.com/zopencommunity/llamacppport/releases&quot;&gt;pax.Z release&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my example below, I used the model “Llama-2-7b-chat”. I downloaded the little endian model that was already converted and quantized from &lt;a href=&quot;https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML&quot;&gt;https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I used zopen’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; to download it to z/OS as follows:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl -L -O https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML/resolve/main/llama-2-7b-chat.ggmlv3.q4_0.bin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note, to proceed to the next steps, you will need at least 4GB of above the bar memory to proceed. Adjust your &lt;a href=&quot;https://www.ibm.com/docs/en/zos/2.5.0?topic=statement-memlimit-parameter&quot;&gt;MEMLIMIT&lt;/a&gt; if necessary.&lt;/p&gt;

&lt;p&gt;Now we’re ready to try it out!&lt;/p&gt;

&lt;p&gt;To demonstrate a typical financial scenario, I asked it to calculate the total expenses as follows:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I have 1000 employees. Each one makes $80k/year. I also pay $100k/year for my property. What are my yearly expenses?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I issued it as follows:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PATH_TO_LLAMACPP/main -m llama-2-7b-chat.ggmlv3.q4_0.bin -n 128 &quot;I have 1000 employees. Each one makes \$80k/year. I also pay \$100k/year for my property. What are my yearly expenses?&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The result was:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;system_info: n_threads = 12 / 24 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 0 | ARM_FMA = 0 | F16C = 0 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.800000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = 128, n_keep = 0


 I have 1000 employees. Each one makes $80k/year. I also pay $100k/year for my property. What are my year expenses?
$100,000 (property) + ($80,000 x 1000 employees) = $8,100,000

So your yearly expenses are $8,100,000. [end of text]

llama_print_timings:        load time = 22215.58 ms
llama_print_timings:      sample time =    98.91 ms /    60 runs   (    1.65 ms per token,   606.60 tokens per second)
llama_print_timings: prompt eval time = 24075.94 ms /    41 tokens (  587.22 ms per token,     1.70 tokens per second)
llama_print_timings:        eval time = 34295.69 ms /    59 runs   (  581.28 ms per token,     1.72 tokens per second)
llama_print_timings:       total time = 58579.91 ms
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Terrific! It worked!&lt;/p&gt;

&lt;p&gt;Now let’s make it an interactive session! And let’s see if it can write code for us!&lt;/p&gt;

&lt;p&gt;Let’s ask it to write a C program that prints out Hello World.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./bin/main -m ../llama-2-7b-chat.ggmlv3.q4_0.bin -n 125 -i -p &quot;[INST] &amp;lt;&amp;lt;SYS&amp;gt;&amp;gt; You are a helpful, respectful and honest assistant. &amp;lt;&amp;lt;/SYS&amp;gt;&amp;gt; Write a C program that prints Hello World. [/INST]&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i&lt;/code&gt; option make it interactive.&lt;/p&gt;

&lt;p&gt;This is the output that we got:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;INST] &amp;lt;&amp;lt;SYS&amp;gt;&amp;gt; You are a helpful, respectful and honest assistant. &amp;lt;&amp;lt;/SYS&amp;gt;&amp;gt; Write a Java program that prints Hello World. [/INST]  Of course! Here is a simple Java program that prints &quot;Hello World&quot;:

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println(&quot;Hello World!&quot;);
  }
}

To compile and run this program, you will need a Java compiler (such as `javac`) and a Java runtime environment (such as `java`). You can also use an Integrated Development Environment (IDE) such as Eclipse or NetBeans to write, compile, and run your program.

Here is how you could compile and runCan you continue

To compile the program, you can use the following command in the terminal or command prompt:

javac HelloWorld.java

This will create a `HelloWorld.class` file in the current directory, which you can then run using the following command:

java HelloWorld

This will print &quot;Hello World!&quot; to the console.

I hope this helps! Let me know if you have any questions or need further assistance.
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It worked! On a heavily used LPAR with 24 CPUs, I was able to get a token rate of 500ms/token, which was not too bad.&lt;/p&gt;

&lt;p&gt;Now, let’s ask it to turn our unstructured data (text) into structured data (JSON).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[INST] &amp;lt;&amp;lt;SYS&amp;gt;&amp;gt; You are a helpful, respectful and honest assistant. &amp;lt;&amp;lt;/SYS&amp;gt;&amp;gt; Can you create a json file representing the following information: IBM revenue for the twelve months ending June 30, 2023 was 5.25B, a 1.42% increase year-over-year. IBM annual revenue for 2022 was 5.3B, a 5.54% increase from 2021. IBM annual revenue for 2021 was 7.35B, a 3.93% increase from 2020. IBM annual revenue for 2020 was 5.179B, a 4.39% decline from 2019.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We get the following output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;{
  &quot;Revenue&quot;: {
     &quot;Annual Revenue&quot;: [
      {
        &quot;Year&quot;: 2023,
        &quot;Revenue&quot;: 5.25B
      },
      {
        &quot;Year&quot;: 2022,
        &quot;Revenue&quot;: 5.3B
      },
...
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Impressive!&lt;/p&gt;

&lt;p&gt;LLaMa.cpp also features an example chatbot that can be run via it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server&lt;/code&gt; executable.&lt;/p&gt;

&lt;p&gt;Run it as follows:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;server -m llama-2-7b-chat.ggmlv3.q4_0.bin
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s an example dialogue with z/OS running the inference engine!&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;/blog/img/in-post/llamacpp.png&quot; alt=&quot;LLaMa.cpp&quot; style=&quot;float:center;&quot; /&gt;
LLaMa.cpp&apos;s server program running on z/OS
&lt;/p&gt;

&lt;h1 id=&quot;future-work&quot;&gt;Future Work&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;In order to perform the dynamic convertion from little endian to big endian, we had to disable the mmap. Enabling mmap support would allow for the model to load much faster. Revisit Approach 1 so that we can optimize start-up time.&lt;/li&gt;
  &lt;li&gt;Performance improvement considerations: LLaMa.cpp supports the BLAS library for math speedup. It also supports SIMD (AVX2), but this needs to be enabled on s390x.&lt;/li&gt;
  &lt;li&gt;Add an option to convert models to big endian, rather than always dynamically converting to big endian.&lt;/li&gt;
  &lt;li&gt;Upstream the effort to the LLaMa.cpp community&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;special-thanks&quot;&gt;Special Thanks&lt;/h1&gt;
&lt;p&gt;Thank you to Haritha D and Mike Fulton for contributing to this effort!&lt;/p&gt;
</description>
        <pubDate>Tue, 22 Aug 2023 00:00:00 +0000</pubDate>
        <link>https://igortodorovskibm.github.io/blog/2023/08/22/llama-cpp/</link>
        <guid isPermaLink="true">https://igortodorovskibm.github.io/blog/2023/08/22/llama-cpp/</guid>
        
        <category>z/OS</category>
        
        <category>AI</category>
        
        <category>LLaMa</category>
        
        <category>LLaMa.cpp</category>
        
        
      </item>
    
  </channel>
</rss>
