Skip to content

Commit

Permalink
add malloc_info
Browse files Browse the repository at this point in the history
Signed-off-by: Kosuke Morimoto <[email protected]>
  • Loading branch information
kmrmt committed Aug 29, 2023
1 parent 15522cc commit d555f35
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 0 deletions.
101 changes: 101 additions & 0 deletions internal/core/malloc/malloc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//
// Copyright (C) 2019-2023 vdaas.org vald team <[email protected]>
//
// Licensed 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
//
// https://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 malloc provides Go wrapper of malloc_info
package malloc

/*
#include <errno.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
char* strerror2() {
return strerror(errno);
}
*/
import "C"

import (
"encoding/xml"
"unsafe"

"github.com/vdaas/vald/internal/errors"
)

type Size struct {
From int `xml:"from,attr"`
To int `xml:"to,attr"`
Total int `xml:"total,attr"`
Count int `xml:"count,attr"`
}

type Sizes struct {
Size []*Size `xml:"size"`
Unsorted Size `xml:"unsorted"`
}

type Memory struct {
Type string `xml:"fast,attr"`
Count int `xml:"count,attr"`
Size int `xml:"size,attr"`
}

type Heap struct {
Nr string `xml:"nr,attr"`
Sizes Sizes `xml:"sizes"`
Total []*Memory `xml:"total"`
System []*Memory `xml:"system"`
Aspace []*Memory `xml:"aspace"`
}

type MallocInfo struct {
Version string `xml:"version,attr"`
Heap []*Heap `xml:"heap"`
Total []*Memory `xml:"total"`
System []*Memory `xml:"system"`
Aspace []*Memory `xml:"aspace"`
}

func convert(body string) (*MallocInfo, error) {
var m MallocInfo
if err := xml.Unmarshal([]byte(body), &m); err != nil {
return nil, err
}
return &m, nil
}

func GetMallocInfo() (*MallocInfo, error) {
var ptr *C.char
var size C.size_t
in := C.open_memstream(&ptr, &size)
defer func() {
C.fclose(in)
C.free(unsafe.Pointer(ptr))
}()

ret := C.malloc_info(0, in)
switch ret {
case 0:
C.fflush(in)
return convert(C.GoStringN(ptr, C.int(size)))
case -1:
return nil, errors.New(C.GoString(C.strerror2()))
default:
return nil, errors.ErrUnexpectedReturnCode(int(ret))
}
}
142 changes: 142 additions & 0 deletions internal/core/malloc/malloc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package malloc

import (
"reflect"
"testing"

"github.com/vdaas/vald/internal/errors"
"github.com/vdaas/vald/internal/test/goleak"
)

func Test_convert(t *testing.T) {
type args struct {
body string
}
type want struct {
wantM *MallocInfo
err error
}
type test struct {
name string
args args
want want
checkFunc func(want, *MallocInfo, error) error
beforeFunc func(*testing.T, args)
afterFunc func(*testing.T, args)
}
defaultCheckFunc := func(w want, gotM *MallocInfo, err error) error {
if !errors.Is(err, w.err) {
return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err)
}
if !reflect.DeepEqual(gotM, w.wantM) {
return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotM, w.wantM)
}
return nil
}
tests := []test{
// TODO test cases
// {
// name: "test_case_1",
// args: args{
// body: "",
// },
// want: want{},
// checkFunc: defaultCheckFunc,
// beforeFunc: func(t *testing.T, args args) {
// t.Helper()
// },
// afterFunc: func(t *testing.T, args args) {
// t.Helper()
// },
// },
}

for _, tc := range tests {
test := tc
t.Run(test.name, func(tt *testing.T) {
tt.Parallel()
defer goleak.VerifyNone(tt, goleak.IgnoreCurrent())
if test.beforeFunc != nil {
test.beforeFunc(tt, test.args)
}
if test.afterFunc != nil {
defer test.afterFunc(tt, test.args)
}
checkFunc := test.checkFunc
if test.checkFunc == nil {
checkFunc = defaultCheckFunc
}

gotM, err := convert(test.args.body)
if err := checkFunc(test.want, gotM, err); err != nil {
tt.Errorf("error = %v", err)
}

})
}
}

func TestGetMallocInfo(t *testing.T) {
type want struct {
want *MallocInfo
err error
}
type test struct {
name string
want want
checkFunc func(want, *MallocInfo, error) error
beforeFunc func(*testing.T)
afterFunc func(*testing.T)
}
defaultCheckFunc := func(w want, got *MallocInfo, err error) error {
if !errors.Is(err, w.err) {
return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err)
}
if !reflect.DeepEqual(got, w.want) {
return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want)
}
return nil
}
tests := []test{
{
name: "convert type check",
want: want{
want: &MallocInfo{},
err: nil,
},
checkFunc: func(w want, got *MallocInfo, err error) error {
if !errors.Is(err, w.err) {
return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err)
}
if reflect.TypeOf(got) != reflect.TypeOf(w.want) {
return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want)
}
return nil
},
},
}

for _, tc := range tests {
test := tc
t.Run(test.name, func(tt *testing.T) {
tt.Parallel()
defer goleak.VerifyNone(tt, goleak.IgnoreCurrent())
if test.beforeFunc != nil {
test.beforeFunc(tt)
}
if test.afterFunc != nil {
defer test.afterFunc(tt)
}
checkFunc := test.checkFunc
if test.checkFunc == nil {
checkFunc = defaultCheckFunc
}

got, err := GetMallocInfo()
if err := checkFunc(test.want, got, err); err != nil {
tt.Errorf("error = %v", err)
}

})
}
}

0 comments on commit d555f35

Please sign in to comment.