cmd/qiloop/trace.go

Summary

Maintainability
B
4 hrs
Test Coverage
package main

import (
    "bytes"
    "fmt"
    "log"
    "os"
    "os/signal"
    "strconv"
    "sync"
    "syscall"
    "time"

    "github.com/aybabtme/rgbterm"
    "github.com/lugu/qiloop/bus"
    "github.com/lugu/qiloop/bus/net"
    "github.com/lugu/qiloop/bus/services"
    "github.com/lugu/qiloop/bus/session"
    "github.com/lugu/qiloop/type/basic"
    "github.com/lugu/qiloop/type/object"
)

var (
    infos   = make([]services.ServiceInfo, 0)
    metas   = make([]object.MetaObject, 0)
    colored = true
)

func init() {
    stat, err := os.Stdout.Stat()
    if err != nil {
        colored = false
    }
    mode := stat.Mode()
    if (mode&os.ModeDevice == 0) || (mode&os.ModeCharDevice == 0) {
        colored = false
    }
}

func getObject(sess bus.Session, info services.ServiceInfo,
    objectID uint32) (bus.ObjectProxy, error) {

    proxy, err := sess.Proxy(info.Name, objectID)
    if err != nil {
        return nil, fmt.Errorf("connect service (%s): %s", info.Name, err)
    }
    return bus.MakeObject(proxy), nil
}

func msgType(typ uint8) (color, label string) {
    switch typ {
    case net.Call:
        return "{#cccccc}", "call "
    case net.Reply:
        return "{#cccccc}", "reply"
    case net.Error:
        return "{#ff0000}", "error"
    case net.Post:
        return "{#0088ff}", "post "
    case net.Event:
        return "{#0088ff}", "event"
    case net.Capability:
        return "{#ff8800}", "cap  "
    case net.Cancel:
        return "{#ff8800}", "cancl"
    case net.Cancelled:
        return "{#ff8800}", "cnled"
    default:
        return "{#ff0000}", "unkno"
    }
}

func printEvent(e bus.EventTrace, info *services.ServiceInfo,
    meta *object.MetaObject) {

    action, err := meta.ActionName(e.SlotId)
    if err != nil {
        action = fmt.Sprintf("unknown (%d)", e.SlotId)
    }
    ts := time.Unix(e.Timestamp.Tv_sec,
        e.Timestamp.Tv_usec*1000).Format("2006/01/02 15:04:05.0000000")

    var size = -1
    var sig = "unknown"
    var data = []byte{}
    var buf bytes.Buffer
    err = e.Arguments.Write(&buf)
    if err == nil {
        sig, err = basic.ReadString(&buf)
        if err == nil {
            data = buf.Bytes()
            size = len(data)
        }
    }

    color, typ := msgType(uint8(e.Kind))
    nocolor := "{}"
    out := rgbterm.ColorOut
    if !colored {
        color = ""
        nocolor = ""
        out = os.Stdout
    }

    fmt.Fprintf(out, "%s%s [id %8d] [%s %4d bytes] %s.%s: %s%s\n",
        color, ts, e.Id, typ, size, info.Name, action, sig, nocolor)
}

func trace(serverURL, serviceName string, objectID uint32) {

    sess, err := session.NewSession(serverURL)
    if err != nil {
        log.Fatalf("%s: %s", serverURL, err)
    }

    directory, err := services.ServiceDirectory(sess)
    if err != nil {
        log.Fatalf("directory: %s", err)
    }

    serviceList, err := directory.Services()
    if err != nil {
        log.Fatalf("services: %s", err)
    }

    stop := make(chan struct{})

    serviceID, err := strconv.Atoi(serviceName)
    if err != nil {
        serviceID = -1
    } else {
        serviceName = ""
    }

    var group sync.WaitGroup

    for _, info := range serviceList {

        if serviceID != -1 && uint32(serviceID) != info.ServiceId {
            continue
        } else if serviceName != "" && serviceName != info.Name {
            continue
        }

        obj, err := getObject(sess, info, objectID)
        if err != nil {
            log.Printf("cannot trace %s: %s", info.Name, err)
            continue
        }

        group.Add(1)

        go func(info services.ServiceInfo, obj bus.ObjectProxy) {

            defer group.Done()

            err = obj.EnableTrace(true)
            if err != nil {
                log.Fatalf("Failed to start traces: %s.", err)
            }
            defer obj.EnableTrace(false)

            cancel, trace, err := obj.SubscribeTraceObject()
            if err != nil {
                log.Fatalf("Failed to stop stats: %s.", err)
            }
            defer cancel()

            meta, err := obj.MetaObject(objectID)
            if err != nil {
                log.Fatalf("%s: MetaObject: %s.", info.Name, err)
            }

            for {
                select {
                case event, ok := <-trace:
                    if !ok {
                        return
                    }
                    printEvent(event, &info, &meta)
                case <-stop:
                    return
                }

            }
        }(info, obj)
    }

    go func() {
        signalChannel := make(chan os.Signal, 2)
        signal.Notify(signalChannel, os.Interrupt, syscall.SIGINT)
        <-signalChannel
        close(stop)
    }()

    group.Wait()
}