github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/memevent/memory_events.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package memevent implements the memory usage events controller, which
    16  // periodically emits events via the eventchannel.
    17  package memevent
    18  
    19  import (
    20  	"time"
    21  
    22  	"github.com/SagerNet/gvisor/pkg/eventchannel"
    23  	"github.com/SagerNet/gvisor/pkg/log"
    24  	"github.com/SagerNet/gvisor/pkg/metric"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    26  	pb "github.com/SagerNet/gvisor/pkg/sentry/kernel/memevent/memory_events_go_proto"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/usage"
    28  	"github.com/SagerNet/gvisor/pkg/sync"
    29  )
    30  
    31  var totalTicks = metric.MustCreateNewUint64Metric("/memory_events/ticks", false /*sync*/, "Total number of memory event periods that have elapsed since startup.")
    32  var totalEvents = metric.MustCreateNewUint64Metric("/memory_events/events", false /*sync*/, "Total number of memory events emitted.")
    33  
    34  // MemoryEvents describes the configuration for the global memory event emitter.
    35  type MemoryEvents struct {
    36  	k *kernel.Kernel
    37  
    38  	// The period is how often to emit an event. The memory events goroutine
    39  	// will ensure a minimum of one event is emitted per this period, regardless
    40  	// how of much memory usage has changed.
    41  	period time.Duration
    42  
    43  	// Writing to this channel indicates the memory goroutine should stop.
    44  	stop chan struct{}
    45  
    46  	// done is used to signal when the memory event goroutine has exited.
    47  	done sync.WaitGroup
    48  }
    49  
    50  // New creates a new MemoryEvents.
    51  func New(k *kernel.Kernel, period time.Duration) *MemoryEvents {
    52  	return &MemoryEvents{
    53  		k:      k,
    54  		period: period,
    55  		stop:   make(chan struct{}),
    56  	}
    57  }
    58  
    59  // Stop stops the memory usage events emitter goroutine. Stop must not be called
    60  // concurrently with Start and may only be called once.
    61  func (m *MemoryEvents) Stop() {
    62  	close(m.stop)
    63  	m.done.Wait()
    64  }
    65  
    66  // Start starts the memory usage events emitter goroutine. Start must not be
    67  // called concurrently with Stop and may only be called once.
    68  func (m *MemoryEvents) Start() {
    69  	if m.period == 0 {
    70  		return
    71  	}
    72  	m.done.Add(1)
    73  	go m.run() // S/R-SAFE: doesn't interact with saved state.
    74  }
    75  
    76  func (m *MemoryEvents) run() {
    77  	defer m.done.Done()
    78  
    79  	// Emit the first event immediately on startup.
    80  	totalTicks.Increment()
    81  	m.emit()
    82  
    83  	ticker := time.NewTicker(m.period)
    84  	defer ticker.Stop()
    85  
    86  	for {
    87  		select {
    88  		case <-m.stop:
    89  			return
    90  		case <-ticker.C:
    91  			totalTicks.Increment()
    92  			m.emit()
    93  		}
    94  	}
    95  }
    96  
    97  func (m *MemoryEvents) emit() {
    98  	totalPlatform, err := m.k.MemoryFile().TotalUsage()
    99  	if err != nil {
   100  		log.Warningf("Failed to fetch memory usage for memory events: %v", err)
   101  		return
   102  	}
   103  	snapshot, _ := usage.MemoryAccounting.Copy()
   104  	total := totalPlatform + snapshot.Mapped
   105  
   106  	totalEvents.Increment()
   107  	eventchannel.Emit(&pb.MemoryUsageEvent{
   108  		Mapped: snapshot.Mapped,
   109  		Total:  total,
   110  	})
   111  }