If you process thousands of time splits,  it's a good idea using %List or just a simple %String instead of JSON - you can have a time savings by factor of about 10!

/// old line
set res=[]
for t=t(0):int:t(1) do res.%Push($zdt(t\86400_","_(t#86400),3,1))
quit res

/// new line
set res=""
for t=t(0):int:t(1) set res=res_$lb($zdt(t\86400_","_(t#86400),3,1))
quit res

/// or
set res=""
for t=t(0):int:t(1) set res=res_","_$zdt(t\86400_","_(t#86400),3,1)
quit $e(res,2,*)

To see the differences, try loops like this

/// with JSON
s h=$zh f i=1:1:1E5 { s r=[] f j=1:1:10 { d r.%Push("abcd") } } w $zh-h,!

/// with %List
s h=$zh f i=1:1:1E6 { s r="" f j=1:1:10 { s r=r_$lb("abcd") } } w $zh-h,!

/// with %String
s h=$zh f i=1:1:1E6 { s r="" f j=1:1:10 { s r=r_","_"abcd" } } w $zh-h,!
/// start, end: timestamp format
/// int       : the interval in seconds
/// mod       : 0 = use the time value as is
///            +1 = round-up the timestamp to a multiple of <int>
///            -1 = round-down the timestamp to a multiple of <int>
///            
/// return an JSON array: [time1, time2, ... timeN]
/// 
ClassMethod Intervals(start, end, int = 15*60, mod = 0) As %DynamicArray
{
    set res=[], t(0)=$zdth(start,3,1), t(1)=$zdth(end,3,1)
    
    for i=0,1 {
        set t(i)=t(i)*86400+$p(t(i),",",2) 
        if mod,t(i)#int { set t(i)=t(i)-(t(i)#int) set:mod>0 t(i)=t(i)+int }
    }

    for t=t(0):int:t(1) do res.%Push($zdt(t\86400_","_(t#86400),3,1))
    quit res
}

You can edit (or enhance) the above code to give you all matching elements. Below I share a code with you where you can choose the result data type (%List or %String) and the result scope (all the matching elements or just the first match).

/// Find common items of two lists or in two delimited strings
/// (as used in a $piece-function)
/// 
/// itm1: first list (as %List or comma-delimited %String)
/// itm2: other list (as %List or comma-delimited %String)
/// ans : 0 = return a comma-delimited %String with the first match found
///       1 = return a comma-delimited %String with all matches found
///       2 = return a %List with the first match found
///       3 = return a %List with all matches found
///       
/// return value: according to <ans> argument
///       
/// Hint: the "$d(var)," part is only needed if the <itm1> argument is
///       of %List type and can contain an "undefined" element like the
///       second element in $lb(11,,33).
///       
ClassMethod FindCommonItems(itm1, itm2, ans = 0)
{
    set ptr=0, res="", all=ans#2
    set:'$lv(itm1) itm1=$lfs(itm1) set:'$lv(itm2) itm2=$lfs(itm2)
    
    while $listnext(itm1,ptr,val) { if $d(val),$lf(itm2,val) { set res=res_$lb(val) quit:'all } }
    quit $s(ans<2:$lts(res), 1:res)
}

In general, $extract() and $zstrip() are your friends.
If you want to strip ONLY the LAST character, then use this

set data="abc,,"
set $extract(data,*)=""
write data --> abc,

If you want to strip ALL (same) trailing characters, use this

set remove=","
set data1="abc,"
set data2="abc,,,"
set data3="abc,,-,,"
set data1=$zstrip(data1,">",remove)
set data2=$zstrip(data2,">",remove)
set data3=$zstrip(data3,">",remove)

write data1 --> abc
write data2 --> abc
write data3 --> abc,,-

If you know which record is locked (i.e. ^My.Global(123) ) then you can identify the locking process (and therefore the user) in a simple method

Class DC.Lock Extends %RegisteredObject
{
/// For a given (global) reference
/// return the (exclusive) locking processID and username
/// 
/// ref: a global reference, for example: $name(^My.Global(1,2,3))
/// 
/// For other lock types (shared, remote)
/// use the infos obtained by info_types OWNER, MODE, FLAGS and COUNTS, see
/// https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_slock
/// 
ClassMethod Who(ref)
{
    if ^$LOCK(ref,"MODE")="X" {
        set pid=^$LOCK(ref,"OWNER")
        if pid {
            set job=##class(%SYS.ProcessQuery).%OpenId(pid)
            quit {"pid":(pid), "usr":($s(job:job.UserName,1:""))}
        }
        
    } else  { quit {} }
}
}

For example:

set ref=$name(^My.Global(123))
lock +@ref:1
if '$test {
    // in case, the node is locked,
    // check up, by who is the node locked
    set who=##class(DC.Lock).Who(ref)
    write who.%ToJSON() --> {"pid":"2396","usr":"kav"}
}

the above approach ist the right way. And I do not see any problem there:

First, in the very first line (of the question) it's stated: "I need to develop a tool ... what data is being consumed by a certain process, ... to build an automated test scenario.", which means, this will be used during a development and/or test phase to gather informations about the touched globals (for automated tests). So the performance is not an issue.
 
Second, the suggestion of Paul Waterman can always run, assuming the process runs with the required right and flag. One can always provide the required conditions.

Four short lines of Objectscript code


ClassMethod Ping(host)
{
	set cmd="ping "_host
	open cmd:"QR":10
	for {use cmd read ans quit:$zeof  use 0 write ans,!}
	close cmd
}

// some test
do ##class(your.class).Ping("google.com")

Pinging google.com [142.250.185.110] with 32 bytes of data:
Reply from 142.250.185.110: bytes=32 time=25ms TTL=114
Reply from 142.250.185.110: bytes=32 time=26ms TTL=114
Reply from 142.250.185.110: bytes=32 time=19ms TTL=114
Reply from 142.250.185.110: bytes=32 time=19ms TTL=114

Ping statistics for 142.250.185.110:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 19ms, Maximum = 26ms, Average = 22ms