Try starting with strace. When one program calls another, it uses one of the exec system calls. For instance, given the following ruby script:
#!/usr/bin/env ruby
system("ls")
system("df")
I can grep strace output to find out what executables it runs:
$ strace -f ./test.rb 2>&1 | grep exec
execve("./test.rb", ["./test.rb"], [/* 23 vars */]) = 0
execve("/opt/opscode/embedded/bin/ruby", ["ruby", "./test.rb"], [/* 23 vars */]) = 0
[pid 7696] read(5, "ame+. If the +exec_name+\n # is"..., 8192) = 8192
[pid 7698] execve("/bin/ls", ["ls"], [/* 23 vars */] <unfinished ...>
[pid 7698] <... execve resumed> ) = 0
[pid 7700] execve("/bin/df", ["df"], [/* 23 vars */] <unfinished ...>
[pid 7700] <... execve resumed> ) = 0
The -f ensures that strace also traces child processes. At the beginning you can see the shell executing my script and the ruby interpreter. Then we can see that this script also executes /bin/ls and /bin/df. Note that this method will only show you the executables are used at run time, not all executables that might have been used if your runtime state had been different.