Adding GC stats to runmetrics plugin (#1207)

* use derived cumulative where applicable
* add a flag to control derived cumulatives
diff --git a/plugin/runmetrics/example_test.go b/plugin/runmetrics/example_test.go
index d441fbd..d05c02e 100644
--- a/plugin/runmetrics/example_test.go
+++ b/plugin/runmetrics/example_test.go
@@ -57,15 +57,23 @@
 	// output:
 	// mayapp/process/cpu_cgo_calls 42
 	// mayapp/process/cpu_goroutines 42
+	// mayapp/process/gc_cpu_fraction 42
+	// mayapp/process/gc_sys 42
 	// mayapp/process/heap_alloc 42
 	// mayapp/process/heap_idle 42
 	// mayapp/process/heap_inuse 42
 	// mayapp/process/heap_objects 42
 	// mayapp/process/heap_release 42
+	// mayapp/process/last_gc_finished_timestamp 42
 	// mayapp/process/memory_alloc 42
 	// mayapp/process/memory_frees 42
 	// mayapp/process/memory_lookups 42
 	// mayapp/process/memory_malloc 42
+	// mayapp/process/next_gc_heap_size 42
+	// mayapp/process/num_forced_gc 42
+	// mayapp/process/num_gc 42
+	// mayapp/process/other_sys 42
+	// mayapp/process/pause_total 42
 	// mayapp/process/stack_inuse 42
 	// mayapp/process/stack_mcache_inuse 42
 	// mayapp/process/stack_mspan_inuse 42
diff --git a/plugin/runmetrics/producer.go b/plugin/runmetrics/producer.go
index 7991b11..d76bebe 100644
--- a/plugin/runmetrics/producer.go
+++ b/plugin/runmetrics/producer.go
@@ -4,6 +4,7 @@
 	"errors"
 	"runtime"
 	"sync"
+	"time"
 
 	"go.opencensus.io/metric"
 	"go.opencensus.io/metric/metricdata"
@@ -18,18 +19,20 @@
 		options RunMetricOptions
 		reg     *metric.Registry
 
-		memStats *memStats
-		cpuStats *cpuStats
+		deprecatedMemStats *deprecatedMemStats
+		memStats           *memStats
+		cpuStats           *cpuStats
 	}
 
 	// RunMetricOptions allows to configure runtime metrics.
 	RunMetricOptions struct {
-		EnableCPU    bool   // EnableCPU whether CPU metrics shall be recorded
-		EnableMemory bool   // EnableMemory whether memory metrics shall be recorded
-		Prefix       string // Prefix is a custom prefix for metric names
+		EnableCPU            bool   // EnableCPU whether CPU metrics shall be recorded
+		EnableMemory         bool   // EnableMemory whether memory metrics shall be recorded
+		Prefix               string // Prefix is a custom prefix for metric names
+		UseDerivedCumulative bool   // UseDerivedCumulative whether DerivedCumulative metrics should be used
 	}
 
-	memStats struct {
+	deprecatedMemStats struct {
 		memStats runtime.MemStats
 
 		memAlloc   *metric.Int64GaugeEntry
@@ -52,6 +55,49 @@
 		stackMSpanSys    *metric.Int64GaugeEntry
 		stackMCacheInuse *metric.Int64GaugeEntry
 		stackMCacheSys   *metric.Int64GaugeEntry
+
+		otherSys      *metric.Int64GaugeEntry
+		gcSys         *metric.Int64GaugeEntry
+		numGC         *metric.Int64GaugeEntry
+		numForcedGC   *metric.Int64GaugeEntry
+		nextGC        *metric.Int64GaugeEntry
+		lastGC        *metric.Int64GaugeEntry
+		pauseTotalNs  *metric.Int64GaugeEntry
+		gcCPUFraction *metric.Float64Entry
+	}
+
+	memStats struct {
+		memStats runtime.MemStats
+
+		memAlloc   *metric.Int64GaugeEntry
+		memTotal   *metric.Int64DerivedCumulative
+		memSys     *metric.Int64GaugeEntry
+		memLookups *metric.Int64DerivedCumulative
+		memMalloc  *metric.Int64DerivedCumulative
+		memFrees   *metric.Int64DerivedCumulative
+
+		heapAlloc    *metric.Int64GaugeEntry
+		heapSys      *metric.Int64GaugeEntry
+		heapIdle     *metric.Int64GaugeEntry
+		heapInuse    *metric.Int64GaugeEntry
+		heapObjects  *metric.Int64GaugeEntry
+		heapReleased *metric.Int64DerivedCumulative
+
+		stackInuse       *metric.Int64GaugeEntry
+		stackSys         *metric.Int64GaugeEntry
+		stackMSpanInuse  *metric.Int64GaugeEntry
+		stackMSpanSys    *metric.Int64GaugeEntry
+		stackMCacheInuse *metric.Int64GaugeEntry
+		stackMCacheSys   *metric.Int64GaugeEntry
+
+		otherSys      *metric.Int64GaugeEntry
+		gcSys         *metric.Int64GaugeEntry
+		numGC         *metric.Int64DerivedCumulative
+		numForcedGC   *metric.Int64DerivedCumulative
+		nextGC        *metric.Int64GaugeEntry
+		lastGC        *metric.Int64GaugeEntry
+		pauseTotalNs  *metric.Int64DerivedCumulative
+		gcCPUFraction *metric.Float64Entry
 	}
 
 	cpuStats struct {
@@ -77,9 +123,17 @@
 	var err error
 
 	if options.EnableMemory {
-		producer.memStats, err = newMemStats(producer)
-		if err != nil {
-			return err
+		switch options.UseDerivedCumulative {
+		case true:
+			producer.memStats, err = newMemStats(producer)
+			if err != nil {
+				return err
+			}
+		default:
+			producer.deprecatedMemStats, err = newDeprecatedMemStats(producer)
+			if err != nil {
+				return err
+			}
 		}
 	}
 
@@ -122,9 +176,9 @@
 	return p.reg.Read()
 }
 
-func newMemStats(producer *producer) (*memStats, error) {
+func newDeprecatedMemStats(producer *producer) (*deprecatedMemStats, error) {
 	var err error
-	memStats := &memStats{}
+	memStats := &deprecatedMemStats{}
 
 	// General
 	memStats.memAlloc, err = producer.createInt64GaugeEntry("process/memory_alloc", "Number of bytes currently allocated in use", metricdata.UnitBytes)
@@ -142,7 +196,7 @@
 		return nil, err
 	}
 
-	memStats.memLookups, err = producer.createInt64GaugeEntry("process/memory_lookups", "Number of pointer lookups performed by the runtime", metricdata.UnitDimensionless)
+	memStats.memLookups, err = producer.createInt64GaugeEntry("process/memory_lookups", "Cumulative number of pointer lookups performed by the runtime", metricdata.UnitDimensionless)
 	if err != nil {
 		return nil, err
 	}
@@ -183,7 +237,7 @@
 		return nil, err
 	}
 
-	memStats.heapReleased, err = producer.createInt64GaugeEntry("process/heap_release", "The number of objects released from the heap", metricdata.UnitBytes)
+	memStats.heapReleased, err = producer.createInt64GaugeEntry("process/heap_release", "The cumulative number of objects released from the heap", metricdata.UnitBytes)
 	if err != nil {
 		return nil, err
 	}
@@ -219,10 +273,51 @@
 		return nil, err
 	}
 
+	// GC
+	memStats.gcSys, err = producer.createInt64GaugeEntry("process/gc_sys", "Bytes of memory in garbage collection metadatas", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.otherSys, err = producer.createInt64GaugeEntry("process/other_sys", "Bytes of memory in miscellaneous off-heap runtime allocations", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.numGC, err = producer.createInt64GaugeEntry("process/num_gc", "Cumulative count of completed GC cycles", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.numForcedGC, err = producer.createInt64GaugeEntry("process/num_forced_gc", "Cumulative count of GC cycles forced by the application", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.nextGC, err = producer.createInt64GaugeEntry("process/next_gc_heap_size", "Target heap size of the next GC cycle in bytes", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.lastGC, err = producer.createInt64GaugeEntry("process/last_gc_finished_timestamp", "Time the last garbage collection finished, as milliseconds since 1970 (the UNIX epoch)", metricdata.UnitMilliseconds)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.pauseTotalNs, err = producer.createInt64GaugeEntry("process/pause_total", "Cumulative milliseconds spent in GC stop-the-world pauses", metricdata.UnitMilliseconds)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.gcCPUFraction, err = producer.createFloat64GaugeEntry("process/gc_cpu_fraction", "Fraction of this program's available CPU time used by the GC since the program started", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
 	return memStats, nil
 }
 
-func (m *memStats) read() {
+func (m *deprecatedMemStats) read() {
 	runtime.ReadMemStats(&m.memStats)
 
 	m.memAlloc.Set(int64(m.memStats.Alloc))
@@ -245,6 +340,218 @@
 	m.stackMSpanSys.Set(int64(m.memStats.MSpanSys))
 	m.stackMCacheInuse.Set(int64(m.memStats.MCacheInuse))
 	m.stackMCacheSys.Set(int64(m.memStats.MCacheSys))
+
+	m.gcSys.Set(int64(m.memStats.GCSys))
+	m.otherSys.Set(int64(m.memStats.OtherSys))
+	m.numGC.Set(int64(m.memStats.NumGC))
+	m.numForcedGC.Set(int64(m.memStats.NumForcedGC))
+	m.nextGC.Set(int64(m.memStats.NextGC))
+	m.lastGC.Set(int64(m.memStats.LastGC) / int64(time.Millisecond))
+	m.pauseTotalNs.Set(int64(m.memStats.PauseTotalNs) / int64(time.Millisecond))
+	m.gcCPUFraction.Set(m.memStats.GCCPUFraction)
+}
+
+func newMemStats(producer *producer) (*memStats, error) {
+	var err error
+	memStats := &memStats{}
+
+	// General
+	memStats.memAlloc, err = producer.createInt64GaugeEntry("process/memory_alloc", "Number of bytes currently allocated in use", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.memTotal, err = producer.createInt64DerivedCumulative("process/total_memory_alloc", "Number of allocations in total", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.memSys, err = producer.createInt64GaugeEntry("process/sys_memory_alloc", "Number of bytes given to the process to use in total", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.memLookups, err = producer.createInt64DerivedCumulative("process/memory_lookups", "Cumulative number of pointer lookups performed by the runtime", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.memMalloc, err = producer.createInt64DerivedCumulative("process/memory_malloc", "Cumulative count of heap objects allocated", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.memFrees, err = producer.createInt64DerivedCumulative("process/memory_frees", "Cumulative count of heap objects freed", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	// Heap
+	memStats.heapAlloc, err = producer.createInt64GaugeEntry("process/heap_alloc", "Process heap allocation", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.heapSys, err = producer.createInt64GaugeEntry("process/sys_heap", "Bytes of heap memory obtained from the OS", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.heapIdle, err = producer.createInt64GaugeEntry("process/heap_idle", "Bytes in idle (unused) spans", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.heapInuse, err = producer.createInt64GaugeEntry("process/heap_inuse", "Bytes in in-use spans", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.heapObjects, err = producer.createInt64GaugeEntry("process/heap_objects", "The number of objects allocated on the heap", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.heapReleased, err = producer.createInt64DerivedCumulative("process/heap_release", "The cumulative number of objects released from the heap", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	// Stack
+	memStats.stackInuse, err = producer.createInt64GaugeEntry("process/stack_inuse", "Bytes in stack spans", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.stackSys, err = producer.createInt64GaugeEntry("process/sys_stack", "The memory used by stack spans and OS thread stacks", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.stackMSpanInuse, err = producer.createInt64GaugeEntry("process/stack_mspan_inuse", "Bytes of allocated mspan structures", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.stackMSpanSys, err = producer.createInt64GaugeEntry("process/sys_stack_mspan", "Bytes of memory obtained from the OS for mspan structures", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.stackMCacheInuse, err = producer.createInt64GaugeEntry("process/stack_mcache_inuse", "Bytes of allocated mcache structures", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.stackMCacheSys, err = producer.createInt64GaugeEntry("process/sys_stack_mcache", "Bytes of memory obtained from the OS for mcache structures", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	// GC
+	memStats.gcSys, err = producer.createInt64GaugeEntry("process/gc_sys", "Bytes of memory in garbage collection metadatas", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.otherSys, err = producer.createInt64GaugeEntry("process/other_sys", "Bytes of memory in miscellaneous off-heap runtime allocations", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.numGC, err = producer.createInt64DerivedCumulative("process/num_gc", "Cumulative count of completed GC cycles", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.numForcedGC, err = producer.createInt64DerivedCumulative("process/num_forced_gc", "Cumulative count of GC cycles forced by the application", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.nextGC, err = producer.createInt64GaugeEntry("process/next_gc_heap_size", "Target heap size of the next GC cycle in bytes", metricdata.UnitBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.lastGC, err = producer.createInt64GaugeEntry("process/last_gc_finished_timestamp", "Time the last garbage collection finished, as milliseconds since 1970 (the UNIX epoch)", metricdata.UnitMilliseconds)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.pauseTotalNs, err = producer.createInt64DerivedCumulative("process/pause_total", "Cumulative milliseconds spent in GC stop-the-world pauses", metricdata.UnitMilliseconds)
+	if err != nil {
+		return nil, err
+	}
+
+	memStats.gcCPUFraction, err = producer.createFloat64GaugeEntry("process/gc_cpu_fraction", "Fraction of this program's available CPU time used by the GC since the program started", metricdata.UnitDimensionless)
+	if err != nil {
+		return nil, err
+	}
+
+	return memStats, nil
+}
+
+func (m *memStats) read() {
+	runtime.ReadMemStats(&m.memStats)
+
+	m.memAlloc.Set(int64(m.memStats.Alloc))
+
+	_ = m.memTotal.UpsertEntry(func() int64 {
+		return int64(m.memStats.TotalAlloc)
+	})
+
+	m.memSys.Set(int64(m.memStats.Sys))
+
+	_ = m.memLookups.UpsertEntry(func() int64 {
+		return int64(m.memStats.Lookups)
+	})
+
+	_ = m.memMalloc.UpsertEntry(func() int64 {
+		return int64(m.memStats.Mallocs)
+	})
+
+	_ = m.memFrees.UpsertEntry(func() int64 {
+		return int64(m.memStats.Frees)
+	})
+
+	m.heapAlloc.Set(int64(m.memStats.HeapAlloc))
+	m.heapSys.Set(int64(m.memStats.HeapSys))
+	m.heapIdle.Set(int64(m.memStats.HeapIdle))
+	m.heapInuse.Set(int64(m.memStats.HeapInuse))
+
+	_ = m.heapReleased.UpsertEntry(func() int64 {
+		return int64(m.memStats.HeapReleased)
+	})
+
+	m.heapObjects.Set(int64(m.memStats.HeapObjects))
+
+	m.stackInuse.Set(int64(m.memStats.StackInuse))
+	m.stackSys.Set(int64(m.memStats.StackSys))
+	m.stackMSpanInuse.Set(int64(m.memStats.MSpanInuse))
+	m.stackMSpanSys.Set(int64(m.memStats.MSpanSys))
+	m.stackMCacheInuse.Set(int64(m.memStats.MCacheInuse))
+	m.stackMCacheSys.Set(int64(m.memStats.MCacheSys))
+
+	m.gcSys.Set(int64(m.memStats.GCSys))
+	m.otherSys.Set(int64(m.memStats.OtherSys))
+
+	_ = m.numGC.UpsertEntry(func() int64 {
+		return int64(m.memStats.NumGC)
+	})
+
+	_ = m.numForcedGC.UpsertEntry(func() int64 {
+		return int64(m.memStats.NumForcedGC)
+	})
+
+	m.nextGC.Set(int64(m.memStats.NextGC))
+	m.lastGC.Set(int64(m.memStats.LastGC) / int64(time.Millisecond))
+
+	_ = m.pauseTotalNs.UpsertEntry(func() int64 {
+		return int64(m.memStats.PauseTotalNs) / int64(time.Millisecond)
+	})
+
+	m.gcCPUFraction.Set(m.memStats.GCCPUFraction)
 }
 
 func newCPUStats(producer *producer) (*cpuStats, error) {
@@ -269,6 +576,27 @@
 	c.numCgoCalls.Set(runtime.NumCgoCall())
 }
 
+func (p *producer) createFloat64GaugeEntry(name string, description string, unit metricdata.Unit) (*metric.Float64Entry, error) {
+	if len(p.options.Prefix) > 0 {
+		name = p.options.Prefix + name
+	}
+
+	gauge, err := p.reg.AddFloat64Gauge(
+		name,
+		metric.WithDescription(description),
+		metric.WithUnit(unit))
+	if err != nil {
+		return nil, errors.New("error creating gauge for " + name + ": " + err.Error())
+	}
+
+	entry, err := gauge.GetEntry()
+	if err != nil {
+		return nil, errors.New("error getting gauge entry for " + name + ": " + err.Error())
+	}
+
+	return entry, nil
+}
+
 func (p *producer) createInt64GaugeEntry(name string, description string, unit metricdata.Unit) (*metric.Int64GaugeEntry, error) {
 	if len(p.options.Prefix) > 0 {
 		name = p.options.Prefix + name
@@ -289,3 +617,19 @@
 
 	return entry, nil
 }
+
+func (p *producer) createInt64DerivedCumulative(name string, description string, unit metricdata.Unit) (*metric.Int64DerivedCumulative, error) {
+	if len(p.options.Prefix) > 0 {
+		name = p.options.Prefix + name
+	}
+
+	cumulative, err := p.reg.AddInt64DerivedCumulative(
+		name,
+		metric.WithDescription(description),
+		metric.WithUnit(unit))
+	if err != nil {
+		return nil, errors.New("error creating gauge for " + name + ": " + err.Error())
+	}
+
+	return cumulative, nil
+}
diff --git a/plugin/runmetrics/producer_test.go b/plugin/runmetrics/producer_test.go
index 5cf2851..322a93c 100644
--- a/plugin/runmetrics/producer_test.go
+++ b/plugin/runmetrics/producer_test.go
@@ -47,6 +47,22 @@
 				{"process/memory_alloc", "process/total_memory_alloc", "process/sys_memory_alloc", "process/memory_lookups", "process/memory_malloc", "process/memory_frees"},
 				{"process/heap_alloc", "process/sys_heap", "process/heap_idle", "process/heap_inuse", "process/heap_objects", "process/heap_release"},
 				{"process/stack_inuse", "process/sys_stack", "process/stack_mspan_inuse", "process/sys_stack_mspan", "process/stack_mcache_inuse", "process/sys_stack_mcache"},
+				{"process/gc_sys", "process/other_sys", "process/num_gc", "process/num_forced_gc", "process/next_gc_heap_size", "process/last_gc_finished_timestamp", "process/pause_total", "process/gc_cpu_fraction"},
+				{"process/cpu_goroutines", "process/cpu_cgo_calls"},
+			},
+			[][]string{},
+		},
+		{
+			"cpu and deprecated memory stats",
+			runmetrics.RunMetricOptions{
+				EnableCPU:    true,
+				EnableMemory: true,
+			},
+			[][]string{
+				{"process/memory_alloc", "process/total_memory_alloc", "process/sys_memory_alloc", "process/memory_lookups", "process/memory_malloc", "process/memory_frees"},
+				{"process/heap_alloc", "process/sys_heap", "process/heap_idle", "process/heap_inuse", "process/heap_objects", "process/heap_release"},
+				{"process/stack_inuse", "process/sys_stack", "process/stack_mspan_inuse", "process/sys_stack_mspan", "process/stack_mcache_inuse", "process/sys_stack_mcache"},
+				{"process/gc_sys", "process/other_sys", "process/num_gc", "process/num_forced_gc", "process/next_gc_heap_size", "process/last_gc_finished_timestamp", "process/pause_total", "process/gc_cpu_fraction"},
 				{"process/cpu_goroutines", "process/cpu_cgo_calls"},
 			},
 			[][]string{},
@@ -64,6 +80,7 @@
 				{"process/memory_alloc", "process/total_memory_alloc", "process/sys_memory_alloc", "process/memory_lookups", "process/memory_malloc", "process/memory_frees"},
 				{"process/heap_alloc", "process/sys_heap", "process/heap_idle", "process/heap_inuse", "process/heap_objects", "process/heap_release"},
 				{"process/stack_inuse", "process/sys_stack", "process/stack_mspan_inuse", "process/sys_stack_mspan", "process/stack_mcache_inuse", "process/sys_stack_mcache"},
+				{"process/gc_sys", "process/other_sys", "process/num_gc", "process/num_forced_gc", "process/next_gc_heap_size", "process/last_gc_finished_timestamp", "process/pause_total", "process/gc_cpu_fraction"},
 			},
 		},
 		{
@@ -76,12 +93,47 @@
 				{"process/memory_alloc", "process/total_memory_alloc", "process/sys_memory_alloc", "process/memory_lookups", "process/memory_malloc", "process/memory_frees"},
 				{"process/heap_alloc", "process/sys_heap", "process/heap_idle", "process/heap_inuse", "process/heap_objects", "process/heap_release"},
 				{"process/stack_inuse", "process/sys_stack", "process/stack_mspan_inuse", "process/sys_stack_mspan", "process/stack_mcache_inuse", "process/sys_stack_mcache"},
+				{"process/gc_sys", "process/other_sys", "process/num_gc", "process/num_forced_gc", "process/next_gc_heap_size", "process/last_gc_finished_timestamp", "process/pause_total", "process/gc_cpu_fraction"},
 			},
 			[][]string{
 				{"process/cpu_goroutines", "process/cpu_cgo_calls"},
 			},
 		},
 		{
+			"only deprecated memory stats",
+			runmetrics.RunMetricOptions{
+				EnableCPU:            false,
+				EnableMemory:         true,
+				UseDerivedCumulative: true,
+			},
+			[][]string{
+				{"process/memory_alloc", "process/total_memory_alloc", "process/sys_memory_alloc", "process/memory_lookups", "process/memory_malloc", "process/memory_frees"},
+				{"process/heap_alloc", "process/sys_heap", "process/heap_idle", "process/heap_inuse", "process/heap_objects", "process/heap_release"},
+				{"process/stack_inuse", "process/sys_stack", "process/stack_mspan_inuse", "process/sys_stack_mspan", "process/stack_mcache_inuse", "process/sys_stack_mcache"},
+				{"process/gc_sys", "process/other_sys", "process/num_gc", "process/num_forced_gc", "process/next_gc_heap_size", "process/last_gc_finished_timestamp", "process/pause_total", "process/gc_cpu_fraction"},
+			},
+			[][]string{
+				{"process/cpu_goroutines", "process/cpu_cgo_calls"},
+			},
+		},
+		{
+			"cpu and deprecated memory stats with custom prefix",
+			runmetrics.RunMetricOptions{
+				EnableCPU:            true,
+				EnableMemory:         true,
+				UseDerivedCumulative: true,
+				Prefix:               "test_",
+			},
+			[][]string{
+				{"test_process/memory_alloc", "test_process/total_memory_alloc", "test_process/sys_memory_alloc", "test_process/memory_lookups", "test_process/memory_malloc", "test_process/memory_frees"},
+				{"test_process/heap_alloc", "test_process/sys_heap", "test_process/heap_idle", "test_process/heap_inuse", "test_process/heap_objects", "test_process/heap_release"},
+				{"test_process/stack_inuse", "test_process/sys_stack", "test_process/stack_mspan_inuse", "test_process/sys_stack_mspan", "test_process/stack_mcache_inuse", "test_process/sys_stack_mcache"},
+				{"test_process/gc_sys", "test_process/other_sys", "test_process/num_gc", "test_process/num_forced_gc", "test_process/next_gc_heap_size", "test_process/last_gc_finished_timestamp", "test_process/pause_total", "test_process/gc_cpu_fraction"},
+				{"test_process/cpu_goroutines", "test_process/cpu_cgo_calls"},
+			},
+			[][]string{},
+		},
+		{
 			"cpu and memory stats with custom prefix",
 			runmetrics.RunMetricOptions{
 				EnableCPU:    true,
@@ -92,6 +144,7 @@
 				{"test_process/memory_alloc", "test_process/total_memory_alloc", "test_process/sys_memory_alloc", "test_process/memory_lookups", "test_process/memory_malloc", "test_process/memory_frees"},
 				{"test_process/heap_alloc", "test_process/sys_heap", "test_process/heap_idle", "test_process/heap_inuse", "test_process/heap_objects", "test_process/heap_release"},
 				{"test_process/stack_inuse", "test_process/sys_stack", "test_process/stack_mspan_inuse", "test_process/sys_stack_mspan", "test_process/stack_mcache_inuse", "test_process/sys_stack_mcache"},
+				{"test_process/gc_sys", "test_process/other_sys", "test_process/num_gc", "test_process/num_forced_gc", "test_process/next_gc_heap_size", "test_process/last_gc_finished_timestamp", "test_process/pause_total", "test_process/gc_cpu_fraction"},
 				{"test_process/cpu_goroutines", "test_process/cpu_cgo_calls"},
 			},
 			[][]string{},