diff --git a/go.sum b/go.sum index a5eb2bf..805dffb 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3 github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pbnjay/memory v0.0.0-20201129165224-b12e5d931931 h1:EeWknjeRU+R3O4ghG7XZCpgSfJNStZyEP8aWyQwJM8s= +github.com/pbnjay/memory v0.0.0-20201129165224-b12e5d931931/go.mod h1:RMU2gJXhratVxBDTFeOdNhd540tG57lt9FIUV0YLvIQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/page/page.go b/hash/page/page.go similarity index 96% rename from page/page.go rename to hash/page/page.go index 36cb10f..23e713f 100644 --- a/page/page.go +++ b/hash/page/page.go @@ -63,8 +63,8 @@ func (d *Page) HasData() bool { return d.GetDataSize() > 0 } -// New will create an instance -func New(requestOffset int, pageSize int, +// NewPage will create an instance +func NewPage(requestOffset int, pageSize int, data []interface{}, totalSize int) *Page { remain := totalSize % pageSize diff --git a/page/page_test.go b/hash/page/page_test.go similarity index 94% rename from page/page_test.go rename to hash/page/page_test.go index 1768c2e..a3f77fc 100644 --- a/page/page_test.go +++ b/hash/page/page_test.go @@ -27,7 +27,7 @@ import ( func TestNewDefaultPage(t *testing.T) { data := make([]interface{}, 10) - page := New(121, 10, data, 499) + page := NewPage(121, 10, data, 499) assert.Equal(t, 10, page.GetDataSize()) assert.Equal(t, 121, page.GetOffset()) @@ -37,6 +37,6 @@ func TestNewDefaultPage(t *testing.T) { assert.True(t, page.HasNext()) assert.True(t, page.HasData()) - page = New(492, 10, data, 499) + page = NewPage(492, 10, data, 499) assert.False(t, page.HasNext()) } diff --git a/page/pager.go b/hash/page/pager.go similarity index 100% rename from page/pager.go rename to hash/page/pager.go diff --git a/path/filepath/path.go b/path/filepath/path.go new file mode 100644 index 0000000..f0c1a27 --- /dev/null +++ b/path/filepath/path.go @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gxfilepath + +import ( + "fmt" + "os" +) + +// Exists returns whether the given file or directory exists +func Exists(path string) (bool, error) { + _, err := os.Lstat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + + return false, err +} + +// FileExists checks whether a file exists in the given path. It also fails if +// the path points to a directory or there is an error when trying to check the file. +func FileExists(path string) (bool, error) { + info, err := os.Lstat(path) + if err != nil { + return false, err + } + if info.IsDir() { + return false, fmt.Errorf("%q is a directory", path) + } + + return true, nil +} + +// DirExists checks whether a directory exists in the given path. It also fails +// if the path is a file rather a directory or there is an error checking whether it exists. +func DirExists(path string) (bool, error) { + info, err := os.Lstat(path) + if err != nil { + return false, err + } + if !info.IsDir() { + return false, fmt.Errorf("%q is a file", path) + } + + return true, nil +} diff --git a/path/filepath/path_test.go b/path/filepath/path_test.go new file mode 100644 index 0000000..2064bf7 --- /dev/null +++ b/path/filepath/path_test.go @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gxfilepath + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +func TestFileExists(t *testing.T) { + file := "./path_test.go" + ok, err := Exists(file) + assert.True(t, ok) + assert.Nil(t, err) + ok, err = DirExists(file) + assert.False(t, ok) + assert.NotNil(t, err) + ok, err = FileExists(file) + assert.True(t, ok) + assert.Nil(t, err) + + file = "./path_test1.go" + ok, err = Exists(file) + assert.False(t, ok) + assert.Nil(t, err) + ok, err = DirExists(file) + assert.False(t, ok) + assert.NotNil(t, err) + ok, err = FileExists(file) + assert.False(t, ok) + assert.NotNil(t, err) +} + +func TestDirExists(t *testing.T) { + file := "./" + ok, err := Exists(file) + assert.True(t, ok) + assert.Nil(t, err) + ok, err = DirExists(file) + assert.True(t, ok) + assert.Nil(t, err) + ok, err = FileExists(file) + assert.False(t, ok) + assert.NotNil(t, err) + + file = "./go" + ok, err = Exists(file) + assert.False(t, ok) + assert.Nil(t, err) + ok, err = DirExists(file) + assert.False(t, ok) + assert.NotNil(t, err) + ok, err = FileExists(file) + assert.False(t, ok) + assert.NotNil(t, err) +} diff --git a/runtime/goroutine_test.go b/runtime/goroutine_test.go index 252c5fe..41e82aa 100644 --- a/runtime/goroutine_test.go +++ b/runtime/goroutine_test.go @@ -31,7 +31,7 @@ import ( func TestGoSafe(t *testing.T) { times := int32(1) - wg := sync.WaitGroup{} + var wg sync.WaitGroup GoSafely(&wg, false, func() { @@ -60,7 +60,7 @@ func TestGoSafe(t *testing.T) { func TestGoUnterminated(t *testing.T) { times := uint64(1) - wg := sync.WaitGroup{} + var wg sync.WaitGroup GoUnterminated( func() { if atomic.AddUint64(×, 1) == 2 { diff --git a/runtime/sys.go b/runtime/sys.go index bc75328..78b5ee0 100644 --- a/runtime/sys.go +++ b/runtime/sys.go @@ -28,9 +28,14 @@ import ( ) import ( + "github.com/shirou/gopsutil/mem" "github.com/shirou/gopsutil/process" ) +import ( + "github.com/dubbogo/gost/path/filepath" +) + var ( CurrentPID = os.Getpid() ) @@ -39,6 +44,36 @@ const ( cgroupMemLimitPath = "/sys/fs/cgroup/memory/memory.limit_in_bytes" ) +// GetCPUNum gets current os's cpu number +func GetCPUNum() int { + return runtime.NumCPU() +} + +// GetMemoryStat gets current os's memory size in bytes +func GetMemoryStat() (total, used, free uint64, usedPercent float64) { + stat, err := mem.VirtualMemory() + if err != nil { + return 0, 0, 0, 0 + } + + return stat.Total, stat.Used, stat.Free, stat.UsedPercent +} + +// IsCgroup checks whether current os is a container or not +func IsCgroup() bool { + ok, _ := gxfilepath.Exists(cgroupMemLimitPath) + if ok { + return true + } + + return false +} + +// GetCgroupMemoryLimit returns a container's total memory in bytes +func GetCgroupMemoryLimit() (uint64, error) { + return readUint(cgroupMemLimitPath) +} + // GetThreadNum gets current process's thread number func GetThreadNum() int { return pprof.Lookup("threadcreate").Count() @@ -124,10 +159,6 @@ func readUint(path string) (uint64, error) { return parseUint(strings.TrimSpace(string(v)), 10, 64) } -func GetCgroupMemoryLimit() (uint64, error) { - return readUint(cgroupMemLimitPath) -} - // GetCgroupProcessMemoryPercent gets current process's memory usage percent in cgroup env func GetCgroupProcessMemoryPercent() (float64, error) { p, err := process.NewProcess(int32(os.Getpid())) diff --git a/runtime/sys_test.go b/runtime/sys_test.go index 9d9973f..1c12965 100644 --- a/runtime/sys_test.go +++ b/runtime/sys_test.go @@ -22,7 +22,10 @@ import ( "time" ) -func TestProcessSysStat(t *testing.T) { +func TestSysStat(t *testing.T) { + t.Logf("current os cpu number %d", GetCPUNum()) + total, used, free, usedPercent := GetMemoryStat() + t.Logf("memory: limit %d bytes, used %d bytes, free %d bytes, usedPercent %f", total, used, free, usedPercent) t.Logf("current prcess thread number %d", GetThreadNum()) go func() { time.Sleep(10e9) @@ -40,16 +43,18 @@ func TestProcessSysStat(t *testing.T) { t.Logf("process cpu stat %v", cpu) size := 100 * 1024 * 1024 - bytes := make([]byte, size) - _ = bytes[:size-1] + arr := make([]byte, size) + for idx := range arr { + arr[idx] = byte(idx / 255) + } memoryStat, err := GetProcessMemoryStat() if err != nil { t.Errorf("GetProcessMemoryStat() = error %+v", err) } - t.Logf("process memory usage stat %v", memoryStat) - //if memoryStat <= uint64(size) { - // t.Errorf("memory usage stat %d < %d", memoryStat, size) - //} + //t.Logf("process memory usage stat %v", memoryStat) + if memoryStat <= uint64(size) { + t.Errorf("memory usage stat %d < %d", memoryStat, size) + } memoryUsage, err := GetProcessMemoryPercent() if err != nil { @@ -57,4 +62,17 @@ func TestProcessSysStat(t *testing.T) { } t.Logf("process memory usage percent %v", memoryUsage) + if IsCgroup() { + memoryLimit, err := GetCgroupMemoryLimit() + if err != nil { + t.Errorf("GetCgroupMemoryLimit() = error %+v", err) + } + t.Logf("CGroupMemoryLimit() = %d", memoryLimit) + + memoryPercent, err := GetCgroupProcessMemoryPercent() + if err != nil { + t.Errorf("GetCgroupProcessMemoryPercent(ps:%d) = error %+v", CurrentPID, err) + } + t.Logf("GetCgroupProcessMemoryPercent(ps:%d) = %+v", CurrentPID, memoryPercent) + } } diff --git a/sync/task_pool_test.go b/sync/task_pool_test.go index 3111be2..ffea69b 100644 --- a/sync/task_pool_test.go +++ b/sync/task_pool_test.go @@ -81,8 +81,9 @@ func TestTaskPoolSimple(t *testing.T) { } wg.Wait() - if taskCnt != atomic.LoadInt64(cnt) { - t.Error("want ", taskCnt, " got ", *cnt) + cntValue := atomic.LoadInt64(cnt) + if taskCnt != cntValue { + t.Error("want ", taskCnt, " got ", cntValue) } }