mirror of https://github.com/jetkvm/kvm.git
feat: add sync trace
This commit is contained in:
parent
656df6c910
commit
b04b148a4b
6
Makefile
6
Makefile
|
|
@ -12,7 +12,13 @@ BUILDKIT_FLAVOR := arm-rockchip830-linux-uclibcgnueabihf
|
||||||
BUILDKIT_PATH ?= /opt/jetkvm-native-buildkit
|
BUILDKIT_PATH ?= /opt/jetkvm-native-buildkit
|
||||||
SKIP_NATIVE_IF_EXISTS ?= 0
|
SKIP_NATIVE_IF_EXISTS ?= 0
|
||||||
SKIP_UI_BUILD ?= 0
|
SKIP_UI_BUILD ?= 0
|
||||||
|
ENABLE_SYNC_TRACE ?= 0
|
||||||
|
|
||||||
GO_BUILD_ARGS := -tags netgo,timetzdata,nomsgpack
|
GO_BUILD_ARGS := -tags netgo,timetzdata,nomsgpack
|
||||||
|
ifeq ($(ENABLE_SYNC_TRACE), 1)
|
||||||
|
GO_BUILD_ARGS := $(GO_BUILD_ARGS),synctrace
|
||||||
|
endif
|
||||||
|
|
||||||
GO_RELEASE_BUILD_ARGS := -trimpath $(GO_BUILD_ARGS)
|
GO_RELEASE_BUILD_ARGS := -trimpath $(GO_BUILD_ARGS)
|
||||||
GO_LDFLAGS := \
|
GO_LDFLAGS := \
|
||||||
-s -w \
|
-s -w \
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
//go:build synctrace
|
||||||
|
|
||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jetkvm/kvm/internal/logging"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultLogger = logging.GetSubsystemLogger("synctrace")
|
||||||
|
|
||||||
|
func logTrace(msg string) {
|
||||||
|
if defaultLogger.GetLevel() > zerolog.TraceLevel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logTrack(3).Trace().Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTrack(callerSkip int) *zerolog.Logger {
|
||||||
|
l := *defaultLogger
|
||||||
|
if l.GetLevel() > zerolog.TraceLevel {
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
pc, file, no, ok := runtime.Caller(callerSkip)
|
||||||
|
if ok {
|
||||||
|
l = l.With().
|
||||||
|
Str("file", file).
|
||||||
|
Int("line", no).
|
||||||
|
Logger()
|
||||||
|
|
||||||
|
details := runtime.FuncForPC(pc)
|
||||||
|
if details != nil {
|
||||||
|
l = l.With().
|
||||||
|
Str("func", details.Name()).
|
||||||
|
Logger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
func logLockTrack(i string) *zerolog.Logger {
|
||||||
|
l := logTrack(4).
|
||||||
|
With().
|
||||||
|
Str("index", i).
|
||||||
|
Logger()
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
indexMu sync.Mutex
|
||||||
|
|
||||||
|
lockCount map[string]int = make(map[string]int)
|
||||||
|
unlockCount map[string]int = make(map[string]int)
|
||||||
|
lastLock map[string]time.Time = make(map[string]time.Time)
|
||||||
|
)
|
||||||
|
|
||||||
|
type trackable interface {
|
||||||
|
sync.Locker
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIndex(t trackable) string {
|
||||||
|
ptr := reflect.ValueOf(t).Pointer()
|
||||||
|
return fmt.Sprintf("%x", ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func increaseLockCount(i string) {
|
||||||
|
indexMu.Lock()
|
||||||
|
defer indexMu.Unlock()
|
||||||
|
|
||||||
|
if _, ok := lockCount[i]; !ok {
|
||||||
|
lockCount[i] = 0
|
||||||
|
}
|
||||||
|
lockCount[i]++
|
||||||
|
|
||||||
|
if _, ok := lastLock[i]; !ok {
|
||||||
|
lastLock[i] = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func increaseUnlockCount(i string) {
|
||||||
|
indexMu.Lock()
|
||||||
|
defer indexMu.Unlock()
|
||||||
|
|
||||||
|
if _, ok := unlockCount[i]; !ok {
|
||||||
|
unlockCount[i] = 0
|
||||||
|
}
|
||||||
|
unlockCount[i]++
|
||||||
|
}
|
||||||
|
|
||||||
|
func logLock(t trackable) {
|
||||||
|
i := getIndex(t)
|
||||||
|
increaseLockCount(i)
|
||||||
|
logLockTrack(i).Trace().Msg("locking mutex")
|
||||||
|
}
|
||||||
|
|
||||||
|
func logUnlock(t trackable) {
|
||||||
|
i := getIndex(t)
|
||||||
|
increaseUnlockCount(i)
|
||||||
|
logLockTrack(i).Trace().Msg("unlocking mutex")
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTryLock(t trackable) {
|
||||||
|
i := getIndex(t)
|
||||||
|
logLockTrack(i).Trace().Msg("trying to lock mutex")
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTryLockResult(t trackable, l bool) {
|
||||||
|
if !l {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := getIndex(t)
|
||||||
|
increaseLockCount(i)
|
||||||
|
logLockTrack(i).Trace().Msg("locked mutex")
|
||||||
|
}
|
||||||
|
|
||||||
|
func logRLock(t trackable) {
|
||||||
|
i := getIndex(t)
|
||||||
|
increaseLockCount(i)
|
||||||
|
logLockTrack(i).Trace().Msg("locking mutex for reading")
|
||||||
|
}
|
||||||
|
|
||||||
|
func logRUnlock(t trackable) {
|
||||||
|
i := getIndex(t)
|
||||||
|
increaseUnlockCount(i)
|
||||||
|
logLockTrack(i).Trace().Msg("unlocking mutex for reading")
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTryRLock(t trackable) {
|
||||||
|
i := getIndex(t)
|
||||||
|
logLockTrack(i).Trace().Msg("trying to lock mutex for reading")
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTryRLockResult(t trackable, l bool) {
|
||||||
|
if !l {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := getIndex(t)
|
||||||
|
increaseLockCount(i)
|
||||||
|
logLockTrack(i).Trace().Msg("locked mutex for reading")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
//go:build synctrace
|
||||||
|
|
||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
gosync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mutex is a wrapper around the sync.Mutex
|
||||||
|
type Mutex struct {
|
||||||
|
mu gosync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock locks the mutex
|
||||||
|
func (m *Mutex) Lock() {
|
||||||
|
logLock(m)
|
||||||
|
m.mu.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock unlocks the mutex
|
||||||
|
func (m *Mutex) Unlock() {
|
||||||
|
logUnlock(m)
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock tries to lock the mutex
|
||||||
|
func (m *Mutex) TryLock() bool {
|
||||||
|
logTryLock(m)
|
||||||
|
l := m.mu.TryLock()
|
||||||
|
logTryLockResult(m, l)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWMutex is a wrapper around the sync.RWMutex
|
||||||
|
type RWMutex struct {
|
||||||
|
mu gosync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock locks the mutex
|
||||||
|
func (m *RWMutex) Lock() {
|
||||||
|
logLock(m)
|
||||||
|
m.mu.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock unlocks the mutex
|
||||||
|
func (m *RWMutex) Unlock() {
|
||||||
|
logUnlock(m)
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLock locks the mutex for reading
|
||||||
|
func (m *RWMutex) RLock() {
|
||||||
|
logRLock(m)
|
||||||
|
m.mu.RLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RUnlock unlocks the mutex for reading
|
||||||
|
func (m *RWMutex) RUnlock() {
|
||||||
|
logRUnlock(m)
|
||||||
|
m.mu.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRLock tries to lock the mutex for reading
|
||||||
|
func (m *RWMutex) TryRLock() bool {
|
||||||
|
logTryRLock(m)
|
||||||
|
l := m.mu.TryRLock()
|
||||||
|
logTryRLockResult(m, l)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
//go:build synctrace
|
||||||
|
|
||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
gosync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Once is a wrapper around the sync.Once
|
||||||
|
type Once struct {
|
||||||
|
mu gosync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do calls the function f if and only if Do has not been called before for this instance of Once.
|
||||||
|
func (o *Once) Do(f func()) {
|
||||||
|
logTrace("Doing once")
|
||||||
|
o.mu.Do(f)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
//go:build !synctrace
|
||||||
|
|
||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
gosync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mutex is a wrapper around the sync.Mutex
|
||||||
|
type Mutex struct {
|
||||||
|
mu gosync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock locks the mutex
|
||||||
|
func (m *Mutex) Lock() {
|
||||||
|
m.mu.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock unlocks the mutex
|
||||||
|
func (m *Mutex) Unlock() {
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock tries to lock the mutex
|
||||||
|
func (m *Mutex) TryLock() bool {
|
||||||
|
return m.mu.TryLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RWMutex is a wrapper around the sync.RWMutex
|
||||||
|
type RWMutex struct {
|
||||||
|
mu gosync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock locks the mutex
|
||||||
|
func (m *RWMutex) Lock() {
|
||||||
|
m.mu.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock unlocks the mutex
|
||||||
|
func (m *RWMutex) Unlock() {
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLock locks the mutex for reading
|
||||||
|
func (m *RWMutex) RLock() {
|
||||||
|
m.mu.RLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RUnlock unlocks the mutex for reading
|
||||||
|
func (m *RWMutex) RUnlock() {
|
||||||
|
m.mu.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryRLock tries to lock the mutex for reading
|
||||||
|
func (m *RWMutex) TryRLock() bool {
|
||||||
|
return m.mu.TryRLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock tries to lock the mutex
|
||||||
|
func (m *RWMutex) TryLock() bool {
|
||||||
|
return m.mu.TryLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitGroup is a wrapper around the sync.WaitGroup
|
||||||
|
type WaitGroup struct {
|
||||||
|
wg gosync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a function to the wait group
|
||||||
|
func (w *WaitGroup) Add(delta int) {
|
||||||
|
w.wg.Add(delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done decrements the wait group counter
|
||||||
|
func (w *WaitGroup) Done() {
|
||||||
|
w.wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the wait group to finish
|
||||||
|
func (w *WaitGroup) Wait() {
|
||||||
|
w.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once is a wrapper around the sync.Once
|
||||||
|
type Once struct {
|
||||||
|
mu gosync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do calls the function f if and only if Do has not been called before for this instance of Once.
|
||||||
|
func (o *Once) Do(f func()) {
|
||||||
|
o.mu.Do(f)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
//go:build synctrace
|
||||||
|
|
||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
gosync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitGroup is a wrapper around the sync.WaitGroup
|
||||||
|
type WaitGroup struct {
|
||||||
|
wg gosync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a function to the wait group
|
||||||
|
func (w *WaitGroup) Add(delta int) {
|
||||||
|
logTrace("Adding to wait group")
|
||||||
|
w.wg.Add(delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done decrements the wait group counter
|
||||||
|
func (w *WaitGroup) Done() {
|
||||||
|
logTrace("Done with wait group")
|
||||||
|
w.wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the wait group to finish
|
||||||
|
func (w *WaitGroup) Wait() {
|
||||||
|
logTrace("Waiting for wait group")
|
||||||
|
w.wg.Wait()
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ show_help() {
|
||||||
echo " --skip-ui-build Skip frontend/UI build"
|
echo " --skip-ui-build Skip frontend/UI build"
|
||||||
echo " --skip-native-build Skip native build"
|
echo " --skip-native-build Skip native build"
|
||||||
echo " --disable-docker Disable docker build"
|
echo " --disable-docker Disable docker build"
|
||||||
|
echo " --enable-sync-trace Enable sync trace (do not use in release builds)"
|
||||||
echo " -i, --install Build for release and install the app"
|
echo " -i, --install Build for release and install the app"
|
||||||
echo " --help Display this help message"
|
echo " --help Display this help message"
|
||||||
echo
|
echo
|
||||||
|
|
@ -32,6 +33,7 @@ REMOTE_PATH="/userdata/jetkvm/bin"
|
||||||
SKIP_UI_BUILD=false
|
SKIP_UI_BUILD=false
|
||||||
SKIP_UI_BUILD_RELEASE=0
|
SKIP_UI_BUILD_RELEASE=0
|
||||||
SKIP_NATIVE_BUILD=0
|
SKIP_NATIVE_BUILD=0
|
||||||
|
ENABLE_SYNC_TRACE=0
|
||||||
RESET_USB_HID_DEVICE=false
|
RESET_USB_HID_DEVICE=false
|
||||||
LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}"
|
LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}"
|
||||||
RUN_GO_TESTS=false
|
RUN_GO_TESTS=false
|
||||||
|
|
@ -64,6 +66,11 @@ while [[ $# -gt 0 ]]; do
|
||||||
RESET_USB_HID_DEVICE=true
|
RESET_USB_HID_DEVICE=true
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--enable-sync-trace)
|
||||||
|
ENABLE_SYNC_TRACE=1
|
||||||
|
LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES},synctrace"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--disable-docker)
|
--disable-docker)
|
||||||
BUILD_IN_DOCKER=false
|
BUILD_IN_DOCKER=false
|
||||||
shift
|
shift
|
||||||
|
|
@ -180,7 +187,10 @@ fi
|
||||||
if [ "$INSTALL_APP" = true ]
|
if [ "$INSTALL_APP" = true ]
|
||||||
then
|
then
|
||||||
msg_info "▶ Building release binary"
|
msg_info "▶ Building release binary"
|
||||||
do_make build_release SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD} SKIP_UI_BUILD=${SKIP_UI_BUILD_RELEASE}
|
do_make build_release \
|
||||||
|
SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD} \
|
||||||
|
SKIP_UI_BUILD=${SKIP_UI_BUILD_RELEASE} \
|
||||||
|
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
||||||
|
|
||||||
# Copy the binary to the remote host as if we were the OTA updater.
|
# Copy the binary to the remote host as if we were the OTA updater.
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
|
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
|
||||||
|
|
@ -189,7 +199,10 @@ then
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
|
ssh "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
|
||||||
else
|
else
|
||||||
msg_info "▶ Building development binary"
|
msg_info "▶ Building development binary"
|
||||||
do_make build_dev SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD} SKIP_UI_BUILD=${SKIP_UI_BUILD_RELEASE}
|
do_make build_dev \
|
||||||
|
SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD} \
|
||||||
|
SKIP_UI_BUILD=${SKIP_UI_BUILD_RELEASE} \
|
||||||
|
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
||||||
|
|
||||||
# Kill any existing instances of the application
|
# Kill any existing instances of the application
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
|
ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue