github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/agent/target/service.go (about)

     1  package target
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/mitchellh/go-ps"
    10  	"github.com/pyroscope-io/pyroscope/pkg/agent"
    11  	"github.com/pyroscope-io/pyroscope/pkg/agent/spy"
    12  	"github.com/pyroscope-io/pyroscope/pkg/agent/upstream/remote"
    13  	"github.com/pyroscope-io/pyroscope/pkg/config"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  var (
    18  	ErrNotFound   = errors.New("service is not found")
    19  	ErrNotRunning = errors.New("service is not running")
    20  )
    21  
    22  type service struct {
    23  	logger *logrus.Logger
    24  	target config.Target
    25  	sc     agent.SessionConfig
    26  }
    27  
    28  func newServiceTarget(logger *logrus.Logger, upstream *remote.Remote, t config.Target) *service {
    29  	return &service{
    30  		logger: logger,
    31  		target: t,
    32  		sc: agent.SessionConfig{
    33  			Upstream: upstream,
    34  			AppName:  t.ApplicationName,
    35  			Tags:     t.Tags,
    36  			// TODO(kolesnikovae): target config should support specifying profile types.
    37  			ProfilingTypes:   []spy.ProfileType{spy.ProfileCPU},
    38  			SpyName:          t.SpyName,
    39  			SampleRate:       uint32(t.SampleRate),
    40  			UploadRate:       10 * time.Second,
    41  			WithSubprocesses: t.DetectSubprocesses,
    42  			Logger:           logger,
    43  			// PID to be specified.
    44  		},
    45  	}
    46  }
    47  
    48  func (s *service) attach(ctx context.Context) {
    49  	logger := s.logger.WithFields(logrus.Fields{
    50  		"service-name": s.target.ServiceName,
    51  		"app-name":     s.sc.AppName,
    52  		"spy-name":     s.sc.SpyName})
    53  	pid, err := getPID(s.target.ServiceName)
    54  	if err == nil {
    55  		logger.WithField("pid", pid).Debug("starting session")
    56  		s.sc.Pid = pid
    57  		err = s.wait(ctx)
    58  	}
    59  	if err != nil {
    60  		logger.WithError(err).Error("failed to attach spy to service")
    61  	} else {
    62  		logger.Debug("session ended")
    63  	}
    64  }
    65  
    66  func (s *service) wait(ctx context.Context) error {
    67  	session, err := agent.NewSession(s.sc)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	if err := session.Start(); err != nil {
    72  		return err
    73  	}
    74  	defer session.Stop()
    75  
    76  	t := time.NewTicker(time.Second)
    77  	defer t.Stop()
    78  	for {
    79  		select {
    80  		case <-ctx.Done():
    81  			return nil
    82  		case <-t.C:
    83  			p, err := ps.FindProcess(s.sc.Pid)
    84  			if err != nil {
    85  				return fmt.Errorf("could not find process: %w", err)
    86  			}
    87  			if p == nil && err == nil {
    88  				return nil
    89  			}
    90  		}
    91  	}
    92  }