go.dedis.ch/onet/v4@v4.0.0-pre1/simul/monitor/measure.go (about)

     1  package monitor
     2  
     3  import (
     4  	"encoding/json"
     5  	"net"
     6  	"time"
     7  
     8  	"sync"
     9  
    10  	"go.dedis.ch/onet/v4/log"
    11  	"golang.org/x/xerrors"
    12  )
    13  
    14  // InvalidHostIndex is the default value when the measure is not assigned
    15  // to a specific host
    16  const InvalidHostIndex = -1
    17  
    18  var global struct {
    19  	// Sink is the server address where all measures are transmitted to for
    20  	// further analysis.
    21  	sink string
    22  
    23  	// Structs are encoded through a json encoder.
    24  	encoder    *json.Encoder
    25  	connection net.Conn
    26  
    27  	sync.Mutex
    28  }
    29  
    30  // Measure is an interface for measurements
    31  // Usage:
    32  // 		measure := monitor.SingleMeasure("bandwidth")
    33  // or
    34  //		measure := monitor.NewTimeMeasure("round")
    35  // 		measure.Record()
    36  type Measure interface {
    37  	// Record must be called when you want to send the value
    38  	// over the monitor listening.
    39  	// Implementation of this interface must RESET the value to `0` at the end
    40  	// of Record(). `0` means the initial value / meaning this measure had when
    41  	// created.
    42  	// Example: TimeMeasure.Record() will reset the time to `time.Now()`
    43  	//          CounterIOMeasure.Record() will  reset the counter of the bytes
    44  	//          read / written to 0.
    45  	//          etc
    46  	Record()
    47  }
    48  
    49  // SingleMeasure is a pair name - value we want to send to the monitor.
    50  type singleMeasure struct {
    51  	Name  string
    52  	Value float64
    53  	Host  int
    54  }
    55  
    56  // TimeMeasure represents a measure regarding time: It includes the wallclock
    57  // time, the cpu time + the user time.
    58  type TimeMeasure struct {
    59  	Wall *singleMeasure
    60  	CPU  *singleMeasure
    61  	User *singleMeasure
    62  	// non exported fields
    63  	// name of the time measure (basename)
    64  	name string
    65  	host int
    66  	// last time
    67  	lastWallTime time.Time
    68  }
    69  
    70  // ConnectSink connects to the given endpoint and initialises a json
    71  // encoder. It can be the address of a proxy or a monitoring process.
    72  // Returns an error if it could not connect to the endpoint.
    73  func ConnectSink(addr string) error {
    74  	global.Lock()
    75  	defer global.Unlock()
    76  	if global.connection != nil {
    77  		return xerrors.New("Already connected to an endpoint")
    78  	}
    79  	log.Lvl3("Connecting to:", addr)
    80  	conn, err := net.Dial("tcp", addr)
    81  	if err != nil {
    82  		return xerrors.Errorf("dial: %v", err)
    83  	}
    84  	log.Lvl3("Connected to sink:", addr)
    85  	global.sink = addr
    86  	global.connection = conn
    87  	global.encoder = json.NewEncoder(conn)
    88  	return nil
    89  }
    90  
    91  // RecordSingleMeasure sends the pair name - value to the monitor directly.
    92  func RecordSingleMeasure(name string, value float64) {
    93  	RecordSingleMeasureWithHost(name, value, InvalidHostIndex)
    94  }
    95  
    96  // RecordSingleMeasureWithHost sends the pair name - value with the host index
    97  // to the monitor directly.
    98  func RecordSingleMeasureWithHost(name string, value float64, host int) {
    99  	sm := newSingleMeasureWithHost(name, value, host)
   100  	sm.Record()
   101  }
   102  
   103  func newSingleMeasure(name string, value float64) *singleMeasure {
   104  	return newSingleMeasureWithHost(name, value, InvalidHostIndex)
   105  }
   106  
   107  func newSingleMeasureWithHost(name string, value float64, host int) *singleMeasure {
   108  	return &singleMeasure{
   109  		Name:  name,
   110  		Value: value,
   111  		Host:  host,
   112  	}
   113  }
   114  
   115  func (s *singleMeasure) Record() {
   116  	if err := send(s); err != nil {
   117  		log.Error("Error sending SingleMeasure", s.Name, " to monitor:", err)
   118  	}
   119  }
   120  
   121  // NewTimeMeasure return *TimeMeasure
   122  func NewTimeMeasure(name string) *TimeMeasure {
   123  	return NewTimeMeasureWithHost(name, InvalidHostIndex)
   124  }
   125  
   126  // NewTimeMeasureWithHost makes a time measure bounded to a host index.
   127  func NewTimeMeasureWithHost(name string, host int) *TimeMeasure {
   128  	tm := &TimeMeasure{name: name, host: host}
   129  	tm.reset()
   130  	return tm
   131  }
   132  
   133  // Record sends the measurements to the monitor:
   134  //
   135  // - wall time: *name*_wall
   136  //
   137  // - system time: *name*_system
   138  //
   139  // - user time: *name*_user
   140  func (tm *TimeMeasure) Record() {
   141  	// Wall time measurement
   142  	tm.Wall = newSingleMeasureWithHost(tm.name+"_wall", float64(time.Since(tm.lastWallTime))/1.0e9, tm.host)
   143  	// CPU time measurement
   144  	tm.CPU.Value, tm.User.Value = getDiffRTime(tm.CPU.Value, tm.User.Value)
   145  	// send data
   146  	tm.Wall.Record()
   147  	tm.CPU.Record()
   148  	tm.User.Record()
   149  	// reset timers
   150  	tm.reset()
   151  
   152  }
   153  
   154  // reset reset the time fields of this time measure
   155  func (tm *TimeMeasure) reset() {
   156  	cpuTimeSys, cpuTimeUser := getRTime()
   157  	tm.CPU = newSingleMeasureWithHost(tm.name+"_system", cpuTimeSys, tm.host)
   158  	tm.User = newSingleMeasureWithHost(tm.name+"_user", cpuTimeUser, tm.host)
   159  	tm.lastWallTime = time.Now()
   160  }
   161  
   162  // CounterIO is an interface that can be used to count how many bytes does an
   163  // object have written and how many bytes does it have read. For example it is
   164  // implemented by cothority/network/ Conn  + Host to know how many bytes a
   165  // connection / Host has written /read.
   166  type CounterIO interface {
   167  	// Rx returns the number of bytes read by this interface.
   168  	Rx() uint64
   169  	// Tx returns the number of bytes transmitted / written by this interface.
   170  	Tx() uint64
   171  	// MsgRx returns the number of messages read by this interface.
   172  	MsgRx() uint64
   173  	// MsgTx returns the number of messages transmitted / written by this interface.
   174  	MsgTx() uint64
   175  }
   176  
   177  // CounterIOMeasure is a struct that takes a CounterIO and can send the
   178  // measurements to the monitor. Each time Record() is called, the measurements
   179  // are put back to 0 (while the CounterIO still sends increased bytes number).
   180  type CounterIOMeasure struct {
   181  	name      string
   182  	host      int
   183  	counter   CounterIO
   184  	baseTx    uint64
   185  	baseRx    uint64
   186  	baseMsgTx uint64
   187  	baseMsgRx uint64
   188  }
   189  
   190  // NewCounterIOMeasure returns a CounterIOMeasure fresh. The base value are set
   191  // to the current value of counter.Rx() and counter.Tx().
   192  func NewCounterIOMeasure(name string, counter CounterIO) *CounterIOMeasure {
   193  	return NewCounterIOMeasureWithHost(name, counter, InvalidHostIndex)
   194  }
   195  
   196  // NewCounterIOMeasureWithHost returns a CounterIOMeasure bounded to a host index. The
   197  // base value are set to the current value of counter.Rx() and counter.Tx().
   198  func NewCounterIOMeasureWithHost(name string, counter CounterIO, host int) *CounterIOMeasure {
   199  	return &CounterIOMeasure{
   200  		name:      name,
   201  		host:      host,
   202  		counter:   counter,
   203  		baseTx:    counter.Tx(),
   204  		baseRx:    counter.Rx(),
   205  		baseMsgTx: counter.MsgTx(),
   206  		baseMsgRx: counter.MsgRx(),
   207  	}
   208  }
   209  
   210  // Reset sets the base to the current value of the counter.
   211  func (cm *CounterIOMeasure) Reset() {
   212  	cm.baseTx = cm.counter.Tx()
   213  	cm.baseRx = cm.counter.Rx()
   214  	cm.baseMsgTx = cm.counter.MsgTx()
   215  	cm.baseMsgRx = cm.counter.MsgRx()
   216  }
   217  
   218  // Record send the actual number of bytes read and written (**name**_written &
   219  // **name**_read) and reset the counters.
   220  func (cm *CounterIOMeasure) Record() {
   221  	// creates the read measure
   222  	bRx := cm.counter.Rx()
   223  	// TODO Later on, we might want to do a check on the conversion between
   224  	// uint64 -> float64, as the MAX values are not the same.
   225  	read := newSingleMeasureWithHost(cm.name+"_rx", float64(bRx-cm.baseRx), cm.host)
   226  	// creates the  written measure
   227  	bTx := cm.counter.Tx()
   228  	written := newSingleMeasureWithHost(cm.name+"_tx", float64(bTx-cm.baseTx), cm.host)
   229  
   230  	bMsgRx := cm.counter.MsgRx()
   231  	readMsg := newSingleMeasureWithHost(cm.name+"_msg_rx", float64(bMsgRx-cm.baseMsgRx), cm.host)
   232  	bMsgTx := cm.counter.MsgTx()
   233  	writtenMsg := newSingleMeasureWithHost(cm.name+"_msg_tx", float64(bMsgTx-cm.baseMsgTx), cm.host)
   234  
   235  	// send them
   236  	read.Record()
   237  	written.Record()
   238  	readMsg.Record()
   239  	writtenMsg.Record()
   240  
   241  	// reset counters
   242  	cm.baseRx = bRx
   243  	cm.baseTx = bTx
   244  	cm.baseMsgRx = bMsgRx
   245  	cm.baseMsgTx = bMsgTx
   246  }
   247  
   248  // Send transmits the given struct over the network.
   249  func send(v interface{}) error {
   250  	global.Lock()
   251  	defer global.Unlock()
   252  	if global.connection == nil {
   253  		return xerrors.New("monitor's sink connection not initialized")
   254  	}
   255  	// For a large number of clients (˜10'000), the connection phase
   256  	// can take some time. This is a linear backoff to enable connection
   257  	// even when there are a lot of request:
   258  	var ok bool
   259  	var err error
   260  	for wait := 500; wait < 1000; wait += 100 {
   261  		if err = global.encoder.Encode(v); err == nil {
   262  			ok = true
   263  			break
   264  		}
   265  		log.Lvl1("Couldn't send to monitor-sink:", err)
   266  		time.Sleep(time.Duration(wait) * time.Millisecond)
   267  		continue
   268  	}
   269  	if !ok {
   270  		return xerrors.New("Could not send any measures")
   271  	}
   272  	return nil
   273  }
   274  
   275  // EndAndCleanup sends a message to end the logging and closes the connection
   276  func EndAndCleanup() {
   277  	if err := send(newSingleMeasure("end", 0)); err != nil {
   278  		log.Error("Error while sending 'end' message:", err)
   279  	}
   280  	global.Lock()
   281  	defer global.Unlock()
   282  	if err := global.connection.Close(); err != nil {
   283  		// at least tell that we could not close the connection:
   284  		log.Error("Could not close connection:", err)
   285  	}
   286  	global.connection = nil
   287  }
   288  
   289  // Returns the difference of the given system- and user-time.
   290  func getDiffRTime(tSys, tUsr float64) (tDiffSys, tDiffUsr float64) {
   291  	nowSys, nowUsr := getRTime()
   292  	return nowSys - tSys, nowUsr - tUsr
   293  }