Skip to content
ZstrFindSubstring

ZstrFindSubstring

Description

Find first occurrence of needle in haystack.

Success

Returns pointer to first occurrence or NULL if not found.

Failure

Aborts via LOG_FATAL when either pointer is NULL.

Usage example (Cross-references)

Usage examples (Cross-references)
    }
    
    Zstr ZstrFindSubstring(Zstr haystack, Zstr needle) {
        if (!needle) {
            LOG_FATAL("Invalid arguments");
    Zstr str_find_zstr(const Str *s, Zstr key) {
        ValidateStr(s);
        return ZstrFindSubstring(StrBegin(s), key);
    }
        bool result = false;
    
        if (ZstrFindSubstring(StrBegin(&bv_str), pattern) != NULL) {
            result = true;
        }
    
        bool ok = StrLen(&out) > 0;
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "24 bytes") != NULL);
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "40 bytes") != NULL);
        bool ok = StrLen(&out) > 0;
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "24 bytes") != NULL);
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "40 bytes") != NULL);
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "24 bytes") != NULL);
        ok      = ok && (ZstrFindSubstring(StrBegin(&out), "40 bytes") != NULL);
    
        StrDeinit(&out);
        DebugAllocatorReportLeaks(&dbg, &out);
    
        bool ok = (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        // FormatStackTrace renders frame lines beginning with "#0".
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "#0") != NULL);
        bool ok = (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        // FormatStackTrace renders frame lines beginning with "#0".
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "#0") != NULL);
    
        StrDeinit(&out);
        DebugAllocatorReportLeaks(&dbg, &out);
    
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        // Real emits "#0" and "#1"; the `--i` mutant emits only "#0".
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "#0 ") != NULL);
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        // Real emits "#0" and "#1"; the `--i` mutant emits only "#0".
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "#0 ") != NULL);
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "#1 ") != NULL);
        // Real emits "#0" and "#1"; the `--i` mutant emits only "#0".
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "#0 ") != NULL);
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "#1 ") != NULL);
    
        StrDeinit(&out);
        StrAppendFmt(&ns, "#{} ", n);
    
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        // The valid top frame "#(N-1) " is present; the spurious "#N " is not.
        ok = ok && (ZstrFindSubstring(StrBegin(&out), StrBegin(&ns)) == NULL);
        ok = ok && (ZstrFindSubstring(StrBegin(&out), "leak:") != NULL);
        // The valid top frame "#(N-1) " is present; the spurious "#N " is not.
        ok = ok && (ZstrFindSubstring(StrBegin(&out), StrBegin(&ns)) == NULL);
    
        StrDeinit(&ns);
    
        // opt50 is inside the 64-row window (rendered); opt64 is past it.
        bool ok = ZstrFindSubstring(StrBegin(&out), "--opt50") != NULL &&
                  ZstrFindSubstring(StrBegin(&out), "--opt00") != NULL &&
                  ZstrFindSubstring(StrBegin(&out), "--opt64") == NULL;
        // opt50 is inside the 64-row window (rendered); opt64 is past it.
        bool ok = ZstrFindSubstring(StrBegin(&out), "--opt50") != NULL &&
                  ZstrFindSubstring(StrBegin(&out), "--opt00") != NULL &&
                  ZstrFindSubstring(StrBegin(&out), "--opt64") == NULL;
        if (!ok) {
        bool ok = ZstrFindSubstring(StrBegin(&out), "--opt50") != NULL &&
                  ZstrFindSubstring(StrBegin(&out), "--opt00") != NULL &&
                  ZstrFindSubstring(StrBegin(&out), "--opt64") == NULL;
        if (!ok) {
            WriteFmt("[a1] render cap: window wrong\n{}\n", StrBegin(&out));
        // (Apple's C mangling prepends `_`; substring search matches both
        // "bt_capture_with_helper" and "_bt_capture_with_helper".)
        bool ok = StrLen(&rendered) > 0 && ZstrFindSubstring(StrBegin(&rendered), "bt_capture_with_helper") != NULL &&
                  ZstrFindSubstring(StrBegin(&rendered), "bt_capture_outer") != NULL;
        // "bt_capture_with_helper" and "_bt_capture_with_helper".)
        bool ok = StrLen(&rendered) > 0 && ZstrFindSubstring(StrBegin(&rendered), "bt_capture_with_helper") != NULL &&
                  ZstrFindSubstring(StrBegin(&rendered), "bt_capture_outer") != NULL;
    
    #if FEATURE_SYS_SYMRESOLVE
        // the Linux backend emits source filenames today (via DwarfLines);
        // macOS / Windows only emit names + offsets.
        ok = ok && ZstrFindSubstring(StrBegin(&rendered), "Backtrace.c") != NULL;
    #endif
    
        ok = ok && StrLen(&rendered) > 0;
        ok = ok && ZstrFindSubstring(StrBegin(&rendered), "bt_vec_capture_with_helper") != NULL;
        ok = ok && ZstrFindSubstring(StrBegin(&rendered), "bt_vec_capture_outer") != NULL;
        ok = ok && StrLen(&rendered) > 0;
        ok = ok && ZstrFindSubstring(StrBegin(&rendered), "bt_vec_capture_with_helper") != NULL;
        ok = ok && ZstrFindSubstring(StrBegin(&rendered), "bt_vec_capture_outer") != NULL;
    
        StrDeinit(&rendered);
    
        // Should also contain the helper name through the shared resolver.
        bool ok = StrLen(&out) > 0 && ZstrFindSubstring(StrBegin(&out), "bt_capture_with_helper") != NULL;
    
        StrDeinit(&out);
        bool ok = n >= 2;
        ok      = ok && StrLen(&rendered) > 0;
        ok      = ok && ZstrFindSubstring(StrBegin(&rendered), "cfi_capture_inner") != NULL;
        ok      = ok && ZstrFindSubstring(StrBegin(&rendered), "cfi_capture_outer") != NULL;
        ok      = ok && StrLen(&rendered) > 0;
        ok      = ok && ZstrFindSubstring(StrBegin(&rendered), "cfi_capture_inner") != NULL;
        ok      = ok && ZstrFindSubstring(StrBegin(&rendered), "cfi_capture_outer") != NULL;
    
        StrDeinit(&rendered);
        ok                = ok && r.module_path && r.module_path[0] != '\0';
        ok                = ok && r.symbol_name != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr1_marker_a") != NULL;
    
        SymbolResolverDeinit(&res);
        ok                = ok && resolve_addr(&res, (void *)&sr1_marker_b, &rb);
        ok                = ok && ra.symbol_name && rb.symbol_name;
        ok                = ok && ZstrFindSubstring(ra.symbol_name, "sr1_marker_a") != NULL;
        ok                = ok && ZstrFindSubstring(rb.symbol_name, "sr1_marker_b") != NULL;
        ok                = ok && ra.symbol_value != rb.symbol_value;
        ok                = ok && ra.symbol_name && rb.symbol_name;
        ok                = ok && ZstrFindSubstring(ra.symbol_name, "sr1_marker_a") != NULL;
        ok                = ok && ZstrFindSubstring(rb.symbol_name, "sr1_marker_b") != NULL;
        ok                = ok && ra.symbol_value != rb.symbol_value;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&symres_marker_helper, &r);
        ok                = ok && r.symbol_name != NULL && ZstrFindSubstring(r.symbol_name, "symres_marker_helper") != NULL;
    
        SymbolResolverDeinit(&res);
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_notype_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "sr_notype_marker") != NULL;
        // size-0 symbol: it matched at its exact value, so the offset is zero.
        ok = ok && r.offset == 0;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_cache_data_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "sr_cache_data_marker") != NULL;
    
        SymbolResolverDeinit(&res);
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_bind_global_sized, &r);
        ok                = ok && r.symbol_name != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_global_sized") != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_local_sized") == NULL;
        ok                = ok && r.symbol_name != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_global_sized") != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_local_sized") == NULL;
    
        SymbolResolverDeinit(&res);
        bool           ok = SymbolResolverResolve(&res, (void *)&sr_bind_global_zero, &r);
        ok                = ok && r.symbol_name != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_global_zero") != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_local_zero") == NULL;
        ok                = ok && r.symbol_name != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_global_zero") != NULL;
        ok                = ok && ZstrFindSubstring(r.symbol_name, "sr_bind_local_zero") == NULL;
    
        SymbolResolverDeinit(&res);
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&scd_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "scd_marker") != NULL;
    
        SymbolResolverDeinit(&res);
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&scd_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "scd_marker") != NULL;
        ok                = ok && DebugAllocatorLiveCount(&alloc) > baseline;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)addr, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "subdir_marker") != NULL;
        ok                = ok && r.offset == 0;
        ok                = ok && (r.module_base + r.symbol_value + r.offset) == addr;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)&subdir_marker, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "subdir_marker") != NULL;
        ok                = ok && DebugAllocatorLiveCount(&alloc) > baseline;
        bool           ok = SymbolResolverResolve(&res, (void *)&sidecar_marker, &r);
        ok                = ok && r.module_path && r.module_path[0] != '\0';
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "sidecar_marker") != NULL;
        // Entry address -> offset zero, reconstruction holds against the
        // sidecar-sourced symbol value, and the sidecar's `.symtab` carries a
        // `.debug_line` lives only in the sidecar too: a source file/line for
        // this TU proves the sidecar DWARF was consulted.
        ok = ok && r.source_file && ZstrFindSubstring(r.source_file, "Sidecar.c") != NULL;
        ok = ok && r.source_line > 0;
        // The marker is named only via the opened sidecar, proving the cache
        // entry actually carries one.
        ok = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "sidecar_marker") != NULL;
        ok = ok && DebugAllocatorLiveCount(&alloc) > baseline;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, (void *)addr, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "df_marker_a") != NULL;
        // Entry address: offset 0, reconstruction holds against the DWARF-sourced
        // low_pc, and the body has a real (small, non-zero) size.
            ResolvedSymbol rlast;
            ok = SymbolResolverResolve(&res, (void *)(addr + r.symbol_size - 1), &rlast);
            ok = ok && rlast.symbol_name && ZstrFindSubstring(rlast.symbol_name, "df_marker_a") != NULL;
            ok = ok && rlast.symbol_value == r.symbol_value;
            ok = ok && rlast.offset == r.symbol_size - 1;
        bool           ok = SymbolResolverResolve(&res, (void *)&df_marker_a, &ra);
        ok                = ok && SymbolResolverResolve(&res, (void *)&df_marker_b, &rb);
        ok                = ok && ra.source_file && ZstrFindSubstring(ra.source_file, "DwarfFallback.c") != NULL;
        ok                = ok && rb.source_file && ZstrFindSubstring(rb.source_file, "DwarfFallback.c") != NULL;
        ok                = ok && ra.source_line > 0 && rb.source_line > 0;
        ok                = ok && SymbolResolverResolve(&res, (void *)&df_marker_b, &rb);
        ok                = ok && ra.source_file && ZstrFindSubstring(ra.source_file, "DwarfFallback.c") != NULL;
        ok                = ok && rb.source_file && ZstrFindSubstring(rb.source_file, "DwarfFallback.c") != NULL;
        ok                = ok && ra.source_line > 0 && rb.source_line > 0;
        ok                = ok && ra.source_line != rb.source_line;
        // The name came from DWARF, proving the function table was built (and the
        // line table alongside it).
        ok = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "df_marker_a") != NULL;
        ok = ok && DebugAllocatorLiveCount(&alloc) > baseline;
        ResolvedSymbol r;
        bool           ok = SymbolResolverResolve(&res, addr, &r);
        ok                = ok && r.symbol_name && ZstrFindSubstring(r.symbol_name, "symres_bias_data") != NULL;
        ok                = ok && r.offset == 0;
        ok                = ok && (r.module_base + r.symbol_value + r.offset) == (u64)addr;
        ok                = ok && SymbolResolverResolve(&res, (void *)(base + K), &rk);
        ok                = ok && r0.symbol_name && rk.symbol_name;
        ok                = ok && ZstrFindSubstring(rk.symbol_name, "symres_bias_data") != NULL;
        ok                = ok && rk.symbol_value == r0.symbol_value;
        ok                = ok && rk.offset == K;
        //   - includes a Content-Length: 11 (length of "<h1>hi</h1>")
        //   - ends with the body
        bool ok = StrLen(&wire) > 0 && ZstrFindSubstring(StrBegin(&wire), "HTTP/1.1 200 OK\r\n") == StrBegin(&wire) &&
                  ZstrFindSubstring(StrBegin(&wire), "Content-Type: text/html\r\n") != NULL &&
                  ZstrFindSubstring(StrBegin(&wire), "Content-Length: 11\r\n") != NULL &&
        //   - ends with the body
        bool ok = StrLen(&wire) > 0 && ZstrFindSubstring(StrBegin(&wire), "HTTP/1.1 200 OK\r\n") == StrBegin(&wire) &&
                  ZstrFindSubstring(StrBegin(&wire), "Content-Type: text/html\r\n") != NULL &&
                  ZstrFindSubstring(StrBegin(&wire), "Content-Length: 11\r\n") != NULL &&
                  ZstrFindSubstring(StrBegin(&wire), "\r\n\r\n<h1>hi</h1>") != NULL;
        bool ok = StrLen(&wire) > 0 && ZstrFindSubstring(StrBegin(&wire), "HTTP/1.1 200 OK\r\n") == StrBegin(&wire) &&
                  ZstrFindSubstring(StrBegin(&wire), "Content-Type: text/html\r\n") != NULL &&
                  ZstrFindSubstring(StrBegin(&wire), "Content-Length: 11\r\n") != NULL &&
                  ZstrFindSubstring(StrBegin(&wire), "\r\n\r\n<h1>hi</h1>") != NULL;
                  ZstrFindSubstring(StrBegin(&wire), "Content-Type: text/html\r\n") != NULL &&
                  ZstrFindSubstring(StrBegin(&wire), "Content-Length: 11\r\n") != NULL &&
                  ZstrFindSubstring(StrBegin(&wire), "\r\n\r\n<h1>hi</h1>") != NULL;
    
        StrDeinit(&wire);
        const MachoSymbol *sym = MachoResolveAddress(&m, vaddr);
        bool               ok  = sym != NULL && sym->name != NULL &&
                  ZstrFindSubstring(sym->name, "test_macho_resolves_running_binary_symbol") != NULL;
    
        MachoDeinit(&m);
        if (built && VecLen(&fns.entries) > 0) {
            const DwarfFunction *f = DwarfFunctionsResolve(&fns, file_relative);
            ok                     = f && f->name && ZstrFindSubstring(f->name, expect_name) != NULL;
            DwarfFunctionsDeinit(&fns);
        }
        if (built) {
            const DwarfLineEntry *e = DwarfLinesResolve(&lines, file_relative);
            if (e && e->file && ZstrFindSubstring(e->file, "Dwarf.c") != NULL && e->line > 0) {
                ok = true;
            }
        if (built && VecLen(&fns.entries) > 0) {
            const DwarfFunction *f = DwarfFunctionsResolve(&fns, file_relative);
            ok = f != NULL && f->name != NULL && ZstrFindSubstring(f->name, "dwarf_marker_helper") != NULL;
            DwarfFunctionsDeinit(&fns);
        }
            // Address 0x2000 -> source.c:10
            const DwarfLineEntry *e = DwarfLinesResolve(&lines, 0x2000);
            ok                      = ok && e && e->file && ZstrFindSubstring(e->file, "source.c") != NULL && e->line == 10;
    
            // An address inside the first row's range still maps to line 10.
        ok = ok && DwarfLinesResolve(&fx.lines, 0x3001) == NULL;
        // The file is source.c (default file 1).
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "source.c") != NULL;
    
        lines_fixture_close(&fx);
        // to a constant (e.g. 42) would index past the file table -> empty/NULL
        // file; `f` left at its init 0 would also not select file 2.
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "other.c") != NULL;
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "source.c") == NULL;
        lines_fixture_close(&fx);
        // file; `f` left at its init 0 would also not select file 2.
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "other.c") != NULL;
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "source.c") == NULL;
        lines_fixture_close(&fx);
        return ok;
        bool                  ok = true;
        const DwarfLineEntry *e  = DwarfLinesResolve(&fx.lines, 0xB800);
        ok                       = ok && e && e->file && ZstrFindSubstring(e->file, "source.c") != NULL;
        lines_fixture_close(&fx);
        return ok;
            ok = ok && DwarfLinesResolve(&lines, 0x6001) == NULL;
            // The file string came through.
            ok = ok && e->file && ZstrFindSubstring(e->file, "source.c") != NULL;
            DwarfLinesDeinit(&lines);
        }
    
        const DwarfLineEntry *e = DwarfLinesResolve(&lines, 0x2000);
        ok                      = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
        ok                      = ok && e && e->dir && ZstrFindSubstring(e->dir, "real_dir") != NULL;
        ok                      = ok && e && e->line == 10;
        const DwarfLineEntry *e = DwarfLinesResolve(&lines, 0x2000);
        ok                      = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
        ok                      = ok && e && e->dir && ZstrFindSubstring(e->dir, "real_dir") != NULL;
        ok                      = ok && e && e->line == 10;
        ok                      = ok && e && e->is_stmt == true;
        e  = DwarfLinesResolve(&lines, 0x2008);
        ok = ok && e && e->line == 20;
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
        ok = ok && e && e->dir && ZstrFindSubstring(e->dir, "real_dir") != NULL;
        ok = ok && e && e->line == 20;
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
        ok = ok && e && e->dir && ZstrFindSubstring(e->dir, "real_dir") != NULL;
    
        // Below the first address and at/after end_sequence -> unresolved.
        bool                  ok = true;
        const DwarfLineEntry *e  = DwarfLinesResolve(&lines, 0x3000);
        ok                       = ok && e && e->file && ZstrFindSubstring(e->file, "first.c") != NULL;
        ok                       = ok && e && e->line == 7;
        ok                       = ok && e && e->dir == NULL;
        // Sequence 1: 0x6000 -> second.c:100
        const DwarfLineEntry *e = DwarfLinesResolve(&lines, 0x6000);
        ok                      = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
        ok                      = ok && e && e->line == 100;
        // file=1 and line=1 between sequences).
        e  = DwarfLinesResolve(&lines, 0x7000);
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "first.c") != NULL;
        ok = ok && e && e->line == 5;
        const DwarfLineEntry *e  = DwarfLinesResolve(&lines, 0x2000);
        ok                       = ok && e && e->line == 10;
        ok                       = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
        ok                       = ok && e && e->dir && ZstrFindSubstring(e->dir, "real_dir") != NULL;
        ok                       = ok && e && e->line == 10;
        ok                       = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
        ok                       = ok && e && e->dir && ZstrFindSubstring(e->dir, "real_dir") != NULL;
    
        DwarfLinesDeinit(&lines);
        // so the row exists at 0x4000 with line 42 and file "first.c".
        ok = ok && e && e->address == 0x4000 && e->line == 42;
        ok = ok && e && e->file && ZstrFindSubstring(e->file, "first.c") != NULL;
    
        DwarfLinesDeinit(&lines);
        const DwarfLineEntry *e  = DwarfLinesResolve(&lines, 0x4400);
        ok                       = ok && e && e->address == 0x4400 && e->line == 22;
        ok                       = ok && e && e->file && ZstrFindSubstring(e->file, "second.c") != NULL;
    
        DwarfLinesDeinit(&lines);
            const DwarfLineEntry *e = DwarfLinesResolve(&lines, 0x3300);
            // file present, but dir index 0 -> dir must be NULL, not pool+42.
            ok = ok && e && e->file && ZstrFindSubstring(e->file, "only.c") != NULL;
            ok = ok && e && e->dir == NULL;
            DwarfLinesDeinit(&lines);
Last updated on