diff --git a/internal/cmdflags/cmdflags.go b/internal/cmdflags/cmdflags.go index 502ff5f..690b696 100644 --- a/internal/cmdflags/cmdflags.go +++ b/internal/cmdflags/cmdflags.go @@ -36,6 +36,11 @@ var supportedCmdFlags = []cmdflag{ defaultValue: options.GetNameDefaultValue(options.TopCount), description: "the top count of process to be printed", }, + cmdflag{ + flagName: options.SortedBy, + defaultValue: options.GetNameDefaultValue(options.SortedBy), + description: "the metrics to be sorted when print the snapshot", + }, } // ParseOptions will parse the flags from command line to options. diff --git a/internal/cmdflags/cmdflags_test.go b/internal/cmdflags/cmdflags_test.go index b5fbf6e..c9205e2 100644 --- a/internal/cmdflags/cmdflags_test.go +++ b/internal/cmdflags/cmdflags_test.go @@ -11,7 +11,7 @@ func TestParseOptions(t *testing.T) { ParseOptions(ops) - assert.IsEqual(t, 5, ops.OptionsCount(), "optionsCount should be 5.") + assert.IsEqual(t, 6, ops.OptionsCount(), "optionsCount should be 6.") option1, _ := ops.GetOption(0) option2, _ := ops.GetOption(1) diff --git a/internal/options/options.go b/internal/options/options.go index 1ca99b6..3caca8d 100644 --- a/internal/options/options.go +++ b/internal/options/options.go @@ -30,6 +30,8 @@ const ( ProcessIDs = "pids" // TopCount indicates the limit of print times. TopCount = "top-count" + // SortedBy indicates how to sort the print. + SortedBy = "sorted-by" // AllProcessIDs indicates all process in a system. AllProcessIDs = "-1" @@ -39,6 +41,10 @@ const ( TextOutput = "text" // DefaultTopCount indicates the default print times. DefaultTopCount = "7" + // SortedByCPU will sort the print by CPU. + SortedByCPU = "cpu" + // SortedByMemory will sort the print by memory. + SortedByMemory = "memory" ) var namesToDefaultValues = map[string]string{ @@ -47,6 +53,7 @@ var namesToDefaultValues = map[string]string{ OutputFormat: TextOutput, ProcessIDs: AllProcessIDs, TopCount: DefaultTopCount, + SortedBy: SortedByCPU, } // GetNameDefaultValue will return the default value for option name, or unknown. @@ -136,6 +143,11 @@ func (ops *Options) GetTopCount() int { return ops.getIntOption(TopCount) } +// GetSortedBy will return the metrics sorted by. +func (ops *Options) GetSortedBy() string { + return ops.getStringOption(SortedBy) +} + // GetOption will return the option by index, out of range will return error. func (ops *Options) GetOption(index int) (Option, error) { limit := len(ops.allOptions) diff --git a/internal/options/options_test.go b/internal/options/options_test.go index 4902a68..85a39a0 100644 --- a/internal/options/options_test.go +++ b/internal/options/options_test.go @@ -2,6 +2,7 @@ package options import ( "github.com/Incarnation-p-lee/cachalot/pkg/assert" + "strconv" "testing" ) @@ -173,15 +174,24 @@ func TestIsAllProcessIDsFalse(t *testing.T) { assert.IsFalse(t, ops.IsAllProcessIDs(), "options should not have all process ids") } -func TesGetTopCount(t *testing.T) { - ops := CreateOptions() +func TestGetTopCount(t *testing.T) { + ops, testCount := CreateOptions(), 2 ops.AppendOption(Option{ Key: TopCount, - Val: DefaultTopCount, + Val: strconv.Itoa(testCount), }) - topCount := ops.GetTopCount() + assert.IsEqual(t, testCount, ops.GetTopCount(), "options should have same top count") +} + +func TestGetSortedBy(t *testing.T) { + ops := CreateOptions() + + ops.AppendOption(Option{ + Key: SortedBy, + Val: SortedByMemory, + }) - assert.IsEqual(t, DefaultTopCount, topCount, "options should have same top count") + assert.IsEqual(t, "memory", ops.GetSortedBy(), "options should have same sorted by") } diff --git a/internal/print/print.go b/internal/print/print.go index 97b1e74..31f2db8 100644 --- a/internal/print/print.go +++ b/internal/print/print.go @@ -7,6 +7,7 @@ import ( "github.com/Incarnation-p-lee/cachalot/pkg/snapshot" "internal/options" "log" + "sort" "time" ) @@ -87,8 +88,26 @@ func reconcileSnapshotTopCount(snapshot *snapshot.Snapshot, topCount int) { } } +func reconcileSnapshotSortedBy(snapshot *snapshot.Snapshot, sortedBy string) { + switch sortedBy { + case options.SortedByMemory: + sort.Slice(snapshot.Processes, func(a, b int) bool { + memoryA, memoryB := snapshot.Processes[a].MemoryStat, snapshot.Processes[b].MemoryStat + return memoryA.UsageInPercentage > memoryB.UsageInPercentage + }) + case options.SortedByCPU: + fallthrough + default: + sort.Slice(snapshot.Processes, func(a, b int) bool { + cpuA, cpuB := snapshot.Processes[a].CPUStat, snapshot.Processes[b].CPUStat + return cpuA.UsageInPercentage > cpuB.UsageInPercentage + }) + } +} + func reconcileSnapshot(snapshot *snapshot.Snapshot, ops *options.Options) { reconcileSnapshotTopCount(snapshot, ops.GetTopCount()) + reconcileSnapshotSortedBy(snapshot, ops.GetSortedBy()) } // Snapshot will print the data module of given snapshot. diff --git a/internal/print/print_test.go b/internal/print/print_test.go index 59672e8..423c763 100644 --- a/internal/print/print_test.go +++ b/internal/print/print_test.go @@ -74,3 +74,55 @@ func TestReconcileSnapshot(t *testing.T) { assert.IsEqual(t, topCount, len(testSnapshot.Processes), "process count after reconcile should be the same as top count") } + +func TestReconcileSnapshotSortedByCPU(t *testing.T) { + testProcesses := []snapshot.Process{ + snapshot.Process{ + CPUStat: snapshot.CPUStat{UsageInPercentage: 12.0}, + }, + snapshot.Process{ + CPUStat: snapshot.CPUStat{UsageInPercentage: 21.0}, + }, + snapshot.Process{ + CPUStat: snapshot.CPUStat{UsageInPercentage: 32.0}, + }, + snapshot.Process{ + CPUStat: snapshot.CPUStat{UsageInPercentage: 21.0}, + }, + } + + testSnapshot := snapshot.CreateSnapshot(time.Now(), testProcesses) + reconcileSnapshotSortedBy(&testSnapshot, "cpu") + + for i := 1; i < len(testSnapshot.Processes); i++ { + first, second := testSnapshot.Processes[i-1], testSnapshot.Processes[i] + assert.IsTrue(t, first.CPUStat.UsageInPercentage >= second.CPUStat.UsageInPercentage, + "the processes should be sorted by cpu in desc order") + } +} + +func TestReconcileSnapshotSortedByMemory(t *testing.T) { + testProcesses := []snapshot.Process{ + snapshot.Process{ + MemoryStat: snapshot.MemoryStat{UsageInPercentage: 2.0}, + }, + snapshot.Process{ + MemoryStat: snapshot.MemoryStat{UsageInPercentage: 11.0}, + }, + snapshot.Process{ + MemoryStat: snapshot.MemoryStat{UsageInPercentage: 22.0}, + }, + snapshot.Process{ + MemoryStat: snapshot.MemoryStat{UsageInPercentage: 11.0}, + }, + } + + testSnapshot := snapshot.CreateSnapshot(time.Now(), testProcesses) + reconcileSnapshotSortedBy(&testSnapshot, "memory") + + for i := 1; i < len(testSnapshot.Processes); i++ { + first, second := testSnapshot.Processes[i-1], testSnapshot.Processes[i] + assert.IsTrue(t, first.MemoryStat.UsageInPercentage >= second.MemoryStat.UsageInPercentage, + "the processes should be sorted by memory in desc order") + } +} diff --git a/internal/sampling/sampling_process.go b/internal/sampling/sampling_process.go index 1d34b57..2f1df0b 100644 --- a/internal/sampling/sampling_process.go +++ b/internal/sampling/sampling_process.go @@ -6,7 +6,6 @@ import ( "log" "path" "path/filepath" - "sort" "strconv" "strings" ) @@ -68,10 +67,6 @@ func sampleAllProcesses(ops *options.Options) []snapshot.Process { processes = append(processes, <-processChan) } - sort.Slice(processes, func(a, b int) bool { - return processes[a].CPUStat.UsageInPercentage > processes[b].CPUStat.UsageInPercentage - }) - return processes } diff --git a/internal/sampling/sampling_process_test.go b/internal/sampling/sampling_process_test.go index fd7180b..5616934 100644 --- a/internal/sampling/sampling_process_test.go +++ b/internal/sampling/sampling_process_test.go @@ -34,13 +34,6 @@ func TestSampleAllProcess(t *testing.T) { processes := sampleAllProcesses(ops) assert.IsTrue(t, len(processes) > 0, "all proccess slice count should not be 0.") - - for i := 0; i < len(processes)-1; i++ { - a, b := processes[i], processes[i+1] - - assert.IsTrue(t, a.CPUStat.UsageInPercentage >= b.CPUStat.UsageInPercentage, - "the process usage should be sorted in desc order") - } } func TestSampleOneProcessSnapshotNilOptions(t *testing.T) {