exec.Command does not register error from Go's own pprof tool - go

Here is my code:
cmd := exec.Command("go", "tool", "pprof", "-dot", "-lines", "http://google.com")
out, err := cmd.Output()
if err != nil {
panic(err)
}
println(string(out))
When I run the exact same command in my console, I see:
$ go tool pprof -dot -lines http://google.com
Fetching profile from http://google.com/profilez
Please wait... (30s)
server response: 404 Not Found
However, my go program does not register that this is an error. Oddly, the variable out prints as an empty string and err is nil. What is going on?
To clarify, I am profiling http://google.com to purposefully create an error. I would normally profile a real Go application.

The text
Fetching profile from http://google.com/profilez
Please wait... (30s)
server response: 404 Not Found
is written to stderr. Your program captures stdout, which is empty. Consider calling:
out, err := cmd.CombinedOutput()
to grab both stdout and stderr.
cmd.Output() and cmd.CombinedOutput() return err == nil because the command exits with status zero. Perhaps an issue should be filed requesting that the command exit with non-zero status.

Related

Golang Error - Undeclared name: xxx compiler [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I am writing a snippet of Golang to perform a curl command that was correctly written in bash script. Here is what I have so far (in VSCode):
url="https://<DOMAIN>/blah/blah/blah/" //<--valid url, used in bash script and it worked
cmd := exec.Command("curl", "-s --user <USERNAME> --request GET --url "+url+" --header 'Accept: application/json'")
out, err = cmd.Output()
if err != nil {
fmt.Println("erorr", err)
return false
}
fmt.Println(out)
All of that ends up giving me the errors "undeclared name: err (compile)" and "undeclared name: out (compile)" on the different lines where I use out and err. I saw this format used on a post here so that's where I got this from. I can't figure out the right way to use Stdin or Stdout in this situation. Can anybody help?
Your compiler errors are due to this line, which tries to assign to two variables that (as the error says) are undeclared:
out, err = cmd.Output()
This should probably be a short variable declaration, i.e.:
out, err := cmd.Output()
However, once that's fixed, it still won't work, because you're passing all of your CLI params to curl as a single argument. It should be:
cmd := exec.Command("curl", "-s", "--user", "<USERNAME>", "--request", "GET", "--url", url, "--header", "Accept: application/json")
Though it's fairly unusual to invoke curl from a Go program; typically you'd use the HTTP client that already exists in net/http, so it's likely that all of this code should be replaced with use of the native client.
In your code you have missed the colon ,use := instead of =
url="https://<DOMAIN>/blah/blah/blah/" //<--valid url, used in bash script and it worked
cmd := exec.Command("curl", "-s --user <USERNAME> --request GET --url "+url+" --header 'Accept: application/json'")
out, err := cmd.Output()
if err != nil {
fmt.Println("erorr", err)
return false
}
fmt.Println(out)

How can I get HTTP RPC Server and client to work?

I am trying to send messages from client to server and back using the exact HTTP RPC server/client code given here.
However, when I run the server, my command line becomes blank because the server starts listening using:
err := http.ListenAndServe(":1234", nil)
if err != nil {
fmt.Println(err.Error())
}
In the client code, I need to get an argument from the command line for client to run:
serverAddress := os.Args[1]
However, this argument is not available because the server code makes my command line blank.
How can I get the server and client to work on the same command line window?

Why subprocess "sleep 10" does not terminate?

I need to test if a process terminates and all I have is its pid number. To do so I test if the pseudo file "/proc/<pid>" exist.
Writing a test for this function, I noticed that the process doesn’t terminate as expected.
For the test, I run "sleep 10" as sub-process which should run for 10 seconds. After starting this process, I poll for the disappearance of the pseudo file "/proc/<pid>". That pseudo file never disappear and the termination of the sub-process is not detected.
Testing the code of golang playground reproduces the problem: https://play.golang.org/p/fb4CbXkIjh3.
I checked that the process is created, and that the pid is correct. While checking the process is seen that it turns into <defunct>. It isn’t thus removed.
The questions are the following:
why doesn’t the sub-process terminate ?
how can I change the code to make it terminate ?
package main
import (
"fmt"
"log"
"os"
"os/exec"
"strconv"
"time"
)
func main() {
fmt.Println("Hello, playground")
cmd := exec.Command("sleep", "10")
if err := cmd.Start(); err != nil {
log.Fatal("unexpected error:", err)
}
pidStr := strconv.Itoa(cmd.Process.Pid)
log.Println("sleep pid:", pidStr)
for {
if _, err := os.Stat("/proc/" + pidStr); os.IsNotExist(err) {
log.Println("detect termination of /proc/" + pidStr)
return
}
log.Println("pgm /proc/" + pidStr + " is running")
time.Sleep(3 * time.Second)
}
}
At the OS level, in any POSIX-compatible OS (Unix, Linux, Darwin, etc), a process that has completed, but not yet been collected by its superior, is in "defunct" or "zombie" state. It still exists, but cannot be killed: it is already dead. It exists precisely so that its superior—the process that can call the OS level wait system call—can call the OS level wait system call and see that the process is now dead.
Once its superior has waited for it, the process is truly removed: there is no longer a zombie process hogging that process ID. If you have a /proc file system, this is when the process vanishes from /proc.
In Go, calling cmd.Wait() invokes the OS-level wait call, so that is the way to do this. If you would like to collect the result of this cmd.Wait(), a good way to do that is to send it through a channel.
(If you want to spawn a very long running process and not wait for it, you can disown it, so that you are no longer its superior. The details on doing this correctly are full of OS-specific doodads, such as discarding control ttys, setting sessions, using procctl or prctl, and so on.)

Why Logrus writes logs to Linux logs?

I use Logrus library to write my golang logs to file. The problem is that logrus also writes the logs to Linux system logs. Here is my log output in log configs.
log.SetOutput(os.Stderr)
I do not want Logrus to write application logs to Linux system logs. Any idea about solving this problem?
To write output to file, you can set output to a file handler.
file, err := os.OpenFile(filename, os.O_WRONLY | os.O_CREATE, 0755)
if err != nil {
// handle error
}
logrus.SetOutput(f)
In case you want to introduce more advanced logic to handle the output, you can use this library Lumberjack as log output

golang exec incorrect behavior

I'm using following code segment to get the XML definition of a virtual machine running on XEN Hypervisor. The code is trying to execute the command virsh dumpxml Ubutnu14 which will give the XML of the VM named Ubuntu14
virshCmd := exec.Command("virsh", "dumpxml", "Ubuntu14")
var virshCmdOutput bytes.Buffer
var stderr bytes.Buffer
virshCmd.Stdout = &virshCmdOutput
virshCmd.Stderr = &stderr
err := virshCmd.Run()
if err != nil {
fmt.Println(err)
fmt.Println(stderr.String())
}
fmt.Println(virshCmdOutput.String())
This code always goes into the error condition for the given domain name and I get the following output.
exit status 1
error: failed to get domain 'Ubuntu14'
error: Domain not found: no domain with matching name 'Ubuntu14'
But if I run the standalone command virsh dumpxml Ubuntu14, I get the correct XML definition.
I would appreciate if someone could give me some hints on what I'm doing wrong. My host machine is Ubuntu-16.04 and golang version is go1.6.2 linux/amd64
I expect you are running virsh as a different user in these two scenarios, and since you don't provide any URI, it is connecting to a different libvirtd instance. If you run virsh as non-root, then it'll usually connect to qemu:///session, but if you run virsh as root, then it'll usually connect to qemu:///system. VMs registered against one URI, will not be visible when connecting to the other URI.
BTW, if you're using go, you'd be much better off using the native Go library bindings for libvirt instead of exec'ing virsh. Your "virsh dumpxml" invokation is pretty much equivalent to this:
import (
"github.com/libvirt/libvirt-go"
)
conn, err := libvirt.NewConnect("qemu:///system")
dom, err := conn.LookupDomainByName("Ubuntu14")
xml, err := dom.GetXMLDesc(0)
(obviously do error handling too)

Resources