nightingale1/vendor/github.com/open-falcon/rrdlite/rrd_c.go

307 lines
6.6 KiB
Go

package rrdlite
/*
#include <stdlib.h>
#include "rrd.h"
#include "rrdfunc.h"
#cgo linux CFLAGS: -std=c99 -DRRD_LITE -D_BSD_SOURCE -DHAVE_CONFIG_H -D_POSIX_SOURCE -DNUMVERS=1.4009 -D_LINUX_OS
#cgo darwin CFLAGS: -std=c99 -DRRD_LITE -D_BSD_SOURCE -DHAVE_CONFIG_H -D_POSIX_SOURCE -DNUMVERS=1.4009 -D_DARWIN_OS
#cgo LDFLAGS: -lm
*/
import "C"
import (
"reflect"
"strconv"
"strings"
"time"
"unsafe"
)
func makeCArgs(args []string) []*C.char {
ret := make([]*C.char, len(args))
for i, s := range args {
ret[i] = C.CString(s)
}
return ret
}
func freeCString(s *C.char) {
C.free(unsafe.Pointer(s))
}
func freeArgs(cArgs []*C.char) {
for _, s := range cArgs {
freeCString(s)
}
}
func makeGoError(e *C.char) error {
var null *C.char
if e == null {
return nil
}
return Error(C.GoString(e))
}
func (c *Creator) create() error {
filename := C.CString(c.filename)
defer freeCString(filename)
args := makeCArgs(c.args)
defer freeArgs(args)
e := C.rrdCreate(
filename,
C.ulong(c.step),
C.time_t(c.start.Unix()),
C.int(len(args)),
&args[0],
)
return makeGoError(e)
}
func (u *Updater) update(_args []string) error {
args := makeCArgs(_args)
defer freeArgs(args)
e := C.rrdUpdate(
(*C.char)(u.filename.p()),
(*C.char)(u.template.p()),
C.int(len(args)),
&args[0],
)
return makeGoError(e)
}
var (
oStart = C.CString("-s")
oEnd = C.CString("-e")
oTitle = C.CString("-t")
oVlabel = C.CString("-v")
oWidth = C.CString("-w")
oHeight = C.CString("-h")
oUpperLimit = C.CString("-u")
oLowerLimit = C.CString("-l")
oRigid = C.CString("-r")
oAltAutoscale = C.CString("-A")
oAltAutoscaleMin = C.CString("-J")
oAltAutoscaleMax = C.CString("-M")
oNoGridFit = C.CString("-N")
oLogarithmic = C.CString("-o")
oUnitsExponent = C.CString("-X")
oUnitsLength = C.CString("-L")
oRightAxis = C.CString("--right-axis")
oRightAxisLabel = C.CString("--right-axis-label")
oDaemon = C.CString("--daemon")
oNoLegend = C.CString("-g")
oLazy = C.CString("-z")
oColor = C.CString("-c")
oSlopeMode = C.CString("-E")
oImageFormat = C.CString("-a")
oInterlaced = C.CString("-i")
oBase = C.CString("-b")
oWatermark = C.CString("-W")
oStep = C.CString("--step")
oMaxRows = C.CString("-m")
)
func ftoa(f float64) string {
return strconv.FormatFloat(f, 'e', 10, 64)
}
func ftoc(f float64) *C.char {
return C.CString(ftoa(f))
}
func i64toa(i int64) string {
return strconv.FormatInt(i, 10)
}
func i64toc(i int64) *C.char {
return C.CString(i64toa(i))
}
func u64toa(u uint64) string {
return strconv.FormatUint(u, 10)
}
func u64toc(u uint64) *C.char {
return C.CString(u64toa(u))
}
func itoa(i int) string {
return i64toa(int64(i))
}
func itoc(i int) *C.char {
return i64toc(int64(i))
}
func utoa(u uint) string {
return u64toa(uint64(u))
}
func utoc(u uint) *C.char {
return u64toc(uint64(u))
}
func parseInfoKey(ik string) (kname, kkey string, kid int) {
kid = -1
o := strings.IndexRune(ik, '[')
if o == -1 {
kname = ik
return
}
c := strings.IndexRune(ik[o+1:], ']')
if c == -1 {
kname = ik
return
}
c += o + 1
kname = ik[:o] + ik[c+1:]
kkey = ik[o+1 : c]
if strings.HasPrefix(kname, "ds.") {
return
} else if id, err := strconv.Atoi(kkey); err == nil && id >= 0 {
kid = id
}
return
}
func updateInfoValue(i *C.struct_rrd_info_t, v interface{}) interface{} {
switch i._type {
case C.RD_I_VAL:
return float64(*(*C.rrd_value_t)(unsafe.Pointer(&i.value[0])))
case C.RD_I_CNT:
return uint(*(*C.ulong)(unsafe.Pointer(&i.value[0])))
case C.RD_I_STR:
return C.GoString(*(**C.char)(unsafe.Pointer(&i.value[0])))
case C.RD_I_INT:
return int(*(*C.int)(unsafe.Pointer(&i.value[0])))
case C.RD_I_BLO:
blob := *(*C.rrd_blob_t)(unsafe.Pointer(&i.value[0]))
b := C.GoBytes(unsafe.Pointer(blob.ptr), C.int(blob.size))
if v == nil {
return b
}
return append(v.([]byte), b...)
}
return nil
}
func parseRRDInfo(i *C.rrd_info_t) map[string]interface{} {
defer C.rrd_info_free(i)
r := make(map[string]interface{})
for w := (*C.struct_rrd_info_t)(i); w != nil; w = w.next {
kname, kkey, kid := parseInfoKey(C.GoString(w.key))
v, ok := r[kname]
switch {
case kid != -1:
var a []interface{}
if ok {
a = v.([]interface{})
}
if len(a) < kid+1 {
oldA := a
a = make([]interface{}, kid+1)
copy(a, oldA)
}
a[kid] = updateInfoValue(w, a[kid])
v = a
case kkey != "":
var m map[string]interface{}
if ok {
m = v.(map[string]interface{})
} else {
m = make(map[string]interface{})
}
old, _ := m[kkey]
m[kkey] = updateInfoValue(w, old)
v = m
default:
v = updateInfoValue(w, v)
}
r[kname] = v
}
return r
}
// Info returns information about RRD file.
func Info(filename string) (map[string]interface{}, error) {
fn := C.CString(filename)
defer freeCString(fn)
var i *C.rrd_info_t
err := makeGoError(C.rrdInfo(&i, fn))
if err != nil {
return nil, err
}
return parseRRDInfo(i), nil
}
// Fetch retrieves data from RRD file.
func Fetch(filename, cf string, start, end time.Time, step time.Duration) (FetchResult, error) {
fn := C.CString(filename)
defer freeCString(fn)
cCf := C.CString(cf)
defer freeCString(cCf)
cStart := C.time_t(start.Unix())
cEnd := C.time_t(end.Unix())
cStep := C.ulong(step.Seconds())
var (
cRet C.int
cDsCnt C.ulong
cDsNames **C.char
cData *C.double
)
err := makeGoError(C.rrdFetch(&cRet, fn, cCf, &cStart, &cEnd, &cStep, &cDsCnt, &cDsNames, &cData))
if err != nil {
return FetchResult{filename, cf, start, end, step, nil, 0, nil}, err
}
start = time.Unix(int64(cStart), 0)
end = time.Unix(int64(cEnd), 0)
step = time.Duration(cStep) * time.Second
dsCnt := int(cDsCnt)
dsNames := make([]string, dsCnt)
for i := 0; i < dsCnt; i++ {
dsName := C.arrayGetCString(cDsNames, C.int(i))
dsNames[i] = C.GoString(dsName)
C.free(unsafe.Pointer(dsName))
}
C.free(unsafe.Pointer(cDsNames))
rowCnt := (int(cEnd)-int(cStart))/int(cStep) + 1
valuesLen := dsCnt * rowCnt
var values []float64
sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&values)))
sliceHeader.Cap = valuesLen
sliceHeader.Len = valuesLen
sliceHeader.Data = uintptr(unsafe.Pointer(cData))
return FetchResult{filename, cf, start, end, step, dsNames, rowCnt, values}, nil
}
// FreeValues free values memory allocated by C.
func (r *FetchResult) FreeValues() {
sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&r.values)))
C.free(unsafe.Pointer(sliceHeader.Data))
}
// Values returns copy of internal array of values.
func (r *FetchResult) Values() []float64 {
return append([]float64{}, r.values...)
}