github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/strace/strace.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package strace
    21  
    22  import (
    23  	"fmt"
    24  	"os/exec"
    25  	"os/user"
    26  	"path/filepath"
    27  
    28  	"github.com/snapcore/snapd/dirs"
    29  	"github.com/snapcore/snapd/osutil"
    30  )
    31  
    32  // These syscalls are excluded because they make strace hang on all or
    33  // some architectures (gettimeofday on arm64).
    34  var excludedSyscalls = "!select,pselect6,_newselect,clock_gettime,sigaltstack,gettid,gettimeofday,nanosleep"
    35  
    36  // Command returns how to run strace in the users context with the
    37  // right set of excluded system calls.
    38  func Command(extraStraceOpts []string, traceeCmd ...string) (*exec.Cmd, error) {
    39  	current, err := user.Current()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	sudoPath, err := exec.LookPath("sudo")
    44  	if err != nil {
    45  		return nil, fmt.Errorf("cannot use strace without sudo: %s", err)
    46  	}
    47  
    48  	// Try strace from the snap first, we use new syscalls like
    49  	// "_newselect" that are known to not work with the strace of e.g.
    50  	// ubuntu 14.04.
    51  	//
    52  	// TODO: some architectures do not have some syscalls (e.g.
    53  	// s390x does not have _newselect). In
    54  	// https://github.com/strace/strace/issues/57 options are
    55  	// discussed.  We could use "-e trace=?syscall" but that is
    56  	// only available since strace 4.17 which is not even in
    57  	// ubutnu 17.10.
    58  	var stracePath string
    59  	cand := filepath.Join(dirs.SnapMountDir, "strace-static", "current", "bin", "strace")
    60  	if osutil.FileExists(cand) {
    61  		stracePath = cand
    62  	}
    63  	if stracePath == "" {
    64  		stracePath, err = exec.LookPath("strace")
    65  		if err != nil {
    66  			return nil, fmt.Errorf("cannot find an installed strace, please try 'snap install strace-static'")
    67  		}
    68  	}
    69  
    70  	args := []string{
    71  		sudoPath,
    72  		"-E",
    73  		stracePath,
    74  		"-u", current.Username,
    75  		"-f",
    76  		"-e", excludedSyscalls,
    77  	}
    78  	args = append(args, extraStraceOpts...)
    79  	args = append(args, traceeCmd...)
    80  
    81  	return &exec.Cmd{
    82  		Path: sudoPath,
    83  		Args: args,
    84  	}, nil
    85  }
    86  
    87  // TraceExecCommand returns an exec.Cmd suitable for tracking timings of
    88  // execve{,at}() calls
    89  func TraceExecCommand(straceLogPath string, origCmd ...string) (*exec.Cmd, error) {
    90  	extraStraceOpts := []string{"-ttt", "-e", "trace=execve,execveat", "-o", fmt.Sprintf("%s", straceLogPath)}
    91  
    92  	return Command(extraStraceOpts, origCmd...)
    93  }