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

As others already wrote,

write $order(^$GLOBAL(""))

gives you the first global name accessible from a given namespace. Usually, this will be a percent-global (^%...) but if you want to get the first (or all) global, which resides in a given database, the do the same as above but with extended global access

set dir = "^^c:\databases\mydb\"  // "^^" and "the path to your CACHE/IRIS.dat file"
write $order(^$|dir|GLOBAL(""))

There are several possibilities, using WinSCP  from Windows, Linux and Samba, etc. But if "using Cache codes" (Objectscript) is a  requirement, then the simplest way is: open a TCP-(server)port on the one end and a TCP-(client)port on the other end, write and read the data, close the connections. Voila. The job is done. I do not see any problem there. OK, maybe you need to open those ports. And do not forget, USB-Sticks exists too ;-))

I'm not aware of any $ISJSON() or similar named function but you can easily make your own

ClassMethod IsJSON(str)
{
	try { ret:{}.%FromJSON(str) 1 } catch e { ret:e.Code=3 0 throw e }
}

set a="{name:""John Doe"" "
set b="{""name"":""John Doe"" }"

write ##class(some.class).IsJSON(a) --> 0
write ##class(some.class).IsJSON(b) --> 1
write ##class(some.class).IsJSON()  --> <UNDEFINED>zIsJSON+1^...  *str

As an addition to the above possibilities, you can  ALWAYS use (assumed, you have the rights to access the database where the global lives - this is true for both of the above too) extended global access.

Just one remark, this kind of access is possible but not the recommended way for creating regular classes (databases) rather to occasionally (on demand) access data from other namespace and/or namespaces.

set nsp = "USER"
set myvar = ^|nsp|MyGlobal(1,2,3)

If there is no namespace associated with the database (where the global lives) just use the implicit namespace

set dir = "^^C:\mydatabases\myfolder\"    // win-world, "^^pathToTheDatabase"
set dir = "^^/opt/mydatabases/myfolder/"  // unix-world, 
write ^|dir|myglobal(1,2,3)

// and, of course all other commands and functions
kill ^|dir|myglobal(1)
write $order(^|dir|myglobal(1,2))
set isDef = $data(^|dir|myglobal)