This commit is contained in:
Marc Brooks 2025-11-18 19:21:35 -06:00 committed by GitHub
commit 68748c13c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 188 additions and 78 deletions

View File

@ -112,12 +112,16 @@ tail -f /var/log/jetkvm.log
│ │ ├── cgo/ # C files for the native library (HDMI, Touchscreen, etc.) │ │ ├── cgo/ # C files for the native library (HDMI, Touchscreen, etc.)
│ │ └── eez/ # EEZ Studio Project files (for Touchscreen) │ │ └── eez/ # EEZ Studio Project files (for Touchscreen)
│ ├── network/ # Network implementation │ ├── network/ # Network implementation
│ ├── sync/ # Synchronization primatives with automatic logging (if synctrace enabled)
│ ├── timesync/ # Time sync/NTP implementation │ ├── timesync/ # Time sync/NTP implementation
│ ├── tzdata/ # Timezone data and generation │ ├── tzdata/ # Timezone data and generation
│ ├── udhcpc/ # DHCP implementation │ ├── udhcpc/ # DHCP implementation
│ ├── usbgadget/ # USB gadget │ ├── usbgadget/ # USB gadget
│ ├── utils/ # SSH handling │ ├── utils/ # SSH handling
│ └── websecure/ # TLS certificate management │ └── websecure/ # TLS certificate management
├── pkg/ # External packages that have customizations
│ ├── myip/ # Get public IP information
│ └── nmlite/ # Network link manager
├── resource/ # netboot iso and other resources ├── resource/ # netboot iso and other resources
├── scripts/ # Bash shell scripts for building and deploying ├── scripts/ # Bash shell scripts for building and deploying
└── static/ # (react client build output) └── static/ # (react client build output)
@ -162,7 +166,7 @@ tail -f /var/log/jetkvm.log
```bash ```bash
cd ui cd ui
npm install npm ci
./dev_device.sh <YOUR_DEVICE_IP> ./dev_device.sh <YOUR_DEVICE_IP>
``` ```
@ -195,9 +199,11 @@ ssh root@192.168.1.100 ps aux | grep jetkvm
### View live logs ### View live logs
The file `/var/log/jetkvm*` contains the JetKVM logs. You can view live logs with:
```bash ```bash
ssh root@192.168.1.100 ssh root@192.168.1.100
tail -f /var/log/jetkvm.log tail -f /var/log/jetkvm*
``` ```
### Reset everything (if stuck) ### Reset everything (if stuck)
@ -322,6 +328,28 @@ Or if you want to manually create the symlink use:
mklink /d ui ..\eez\src\ui mklink /d ui ..\eez\src\ui
``` ```
### Build is unstable even before you changed anything
Make sure you clean up your _node_ modules and do an `npm ci` (**not** `npm i`) to ensure that you get the exact packages required by _package-lock.json_. This is especially important when switching branches!
```bash
cd ui && rm -rf node_modules/ && npm ci && cd ..
```
If you are working on upgrades to the UI packages use this command to wipe the slate clean and get a new valid _package-lock.json_:
```bash
cd ui && rm -rf node_modules/ package-lock.json && npm i && cd ..
```
### Device panics or becomes unresponsive
You can also run the device-side _go_ code under a debug session to view the logs as the device is booting up and being used. To do this use the following command in your development command-line (where the IP is the JetKVM device's IP on your network) to see a very detailed `synctrace` of all mutex activity:
```bash
./dev_deploy.sh -r <IP> --enable-sync-trace
```
--- ---
## Next Steps ## Next Steps

View File

@ -8,18 +8,17 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/sync"
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson" "github.com/coder/websocket/wsjson"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/coder/websocket"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )

View File

@ -5,12 +5,13 @@ import (
"fmt" "fmt"
"os" "os"
"strconv" "strconv"
"sync"
"github.com/jetkvm/kvm/internal/confparser" "github.com/jetkvm/kvm/internal/confparser"
"github.com/jetkvm/kvm/internal/logging" "github.com/jetkvm/kvm/internal/logging"
"github.com/jetkvm/kvm/internal/network/types" "github.com/jetkvm/kvm/internal/network/types"
"github.com/jetkvm/kvm/internal/sync"
"github.com/jetkvm/kvm/internal/usbgadget" "github.com/jetkvm/kvm/internal/usbgadget"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
) )

View File

@ -7,9 +7,10 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/sync"
"github.com/prometheus/common/version" "github.com/prometheus/common/version"
) )

View File

@ -4,7 +4,8 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
"sync"
"github.com/jetkvm/kvm/internal/sync"
) )
const ( const (

3
hw.go
View File

@ -6,8 +6,9 @@ import (
"os/exec" "os/exec"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/sync"
) )
func extractSerialNumber() (string, error) { func extractSerialNumber() (string, error) {

View File

@ -5,9 +5,10 @@ import (
"net" "net"
"reflect" "reflect"
"strings" "strings"
"sync"
"github.com/jetkvm/kvm/internal/logging" "github.com/jetkvm/kvm/internal/logging"
"github.com/jetkvm/kvm/internal/sync"
pion_mdns "github.com/pion/mdns/v2" pion_mdns "github.com/pion/mdns/v2"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"

View File

@ -1,9 +1,10 @@
package native package native
import ( import (
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/sync"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )

View File

@ -1,6 +1,6 @@
package native package native
import "sync" import "github.com/jetkvm/kvm/internal/sync"
var ( var (
instance *Native instance *Native

View File

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/jetkvm/kvm/internal/logging" "github.com/jetkvm/kvm/internal/logging"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
@ -47,103 +48,160 @@ func logTrack(callerSkip int) *zerolog.Logger {
return &l return &l
} }
func logLockTrack(i string) *zerolog.Logger { func logLockTrack(ptr uintptr) *zerolog.Logger {
l := logTrack(4). l := logTrack(4).
With(). With().
Str("index", i). Str("ptr", fmt.Sprintf("%x", ptr)).
Logger() Logger()
return &l 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 { type trackable interface {
sync.Locker sync.Locker
} }
func getIndex(t trackable) string { type trackingEntry struct {
ptr := reflect.ValueOf(t).Pointer() lockCount int
return fmt.Sprintf("%x", ptr) unlockCount int
firstLock time.Time
lastLock time.Time
lastUnlock time.Time
} }
func increaseLockCount(i string) { var (
indexMu sync.Mutex
tracking map[uintptr]*trackingEntry = make(map[uintptr]*trackingEntry)
)
func getPointer(t trackable) uintptr {
return reflect.ValueOf(t).Pointer()
}
func increaseLockCount(ptr uintptr) {
indexMu.Lock() indexMu.Lock()
defer indexMu.Unlock() defer indexMu.Unlock()
if _, ok := lockCount[i]; !ok { entry, ok := tracking[ptr]
lockCount[i] = 0 if !ok {
} entry = &trackingEntry{}
lockCount[i]++ entry.firstLock = time.Now()
tracking[ptr] = entry
if _, ok := lastLock[i]; !ok {
lastLock[i] = time.Now()
} }
entry.lockCount++
entry.lastLock = time.Now()
} }
func increaseUnlockCount(i string) { func increaseUnlockCount(ptr uintptr) {
indexMu.Lock() indexMu.Lock()
defer indexMu.Unlock()
if _, ok := unlockCount[i]; !ok { entry, ok := tracking[ptr]
unlockCount[i] = 0 if !ok {
entry = &trackingEntry{}
tracking[ptr] = entry
}
entry.unlockCount++
entry.lastUnlock = time.Now()
delta := entry.lockCount - entry.unlockCount
indexMu.Unlock()
if !ok {
logLockTrack(ptr).Warn().Interface("entry", entry).Msg("Unlock called without prior Lock")
}
if delta < 0 {
logLockTrack(ptr).Warn().Interface("entry", entry).Msg("Unlock called more times than Lock")
} }
unlockCount[i]++
} }
func logLock(t trackable) { func logLock(t trackable) {
i := getIndex(t) ptr := getPointer(t)
increaseLockCount(i) increaseLockCount(ptr)
logLockTrack(i).Trace().Msg("locking mutex") logLockTrack(ptr).Trace().Msg("locking mutex")
} }
func logUnlock(t trackable) { func logUnlock(t trackable) {
i := getIndex(t) ptr := getPointer(t)
increaseUnlockCount(i) increaseUnlockCount(ptr)
logLockTrack(i).Trace().Msg("unlocking mutex") logLockTrack(ptr).Trace().Msg("unlocking mutex")
} }
func logTryLock(t trackable) { func logTryLock(t trackable) {
i := getIndex(t) ptr := getPointer(t)
logLockTrack(i).Trace().Msg("trying to lock mutex") logLockTrack(ptr).Trace().Msg("trying to lock mutex")
} }
func logTryLockResult(t trackable, l bool) { func logTryLockResult(t trackable, l bool) {
if !l { if !l {
return return
} }
i := getIndex(t) ptr := getPointer(t)
increaseLockCount(i) increaseLockCount(ptr)
logLockTrack(i).Trace().Msg("locked mutex") logLockTrack(ptr).Trace().Msg("locked mutex")
} }
func logRLock(t trackable) { func logRLock(t trackable) {
i := getIndex(t) ptr := getPointer(t)
increaseLockCount(i) increaseLockCount(ptr)
logLockTrack(i).Trace().Msg("locking mutex for reading") logLockTrack(ptr).Trace().Msg("locking mutex for reading")
} }
func logRUnlock(t trackable) { func logRUnlock(t trackable) {
i := getIndex(t) ptr := getPointer(t)
increaseUnlockCount(i) increaseUnlockCount(ptr)
logLockTrack(i).Trace().Msg("unlocking mutex for reading") logLockTrack(ptr).Trace().Msg("unlocking mutex for reading")
} }
func logTryRLock(t trackable) { func logTryRLock(t trackable) {
i := getIndex(t) ptr := getPointer(t)
logLockTrack(i).Trace().Msg("trying to lock mutex for reading") logLockTrack(ptr).Trace().Msg("trying to lock mutex for reading")
} }
func logTryRLockResult(t trackable, l bool) { func logTryRLockResult(t trackable, l bool) {
if !l { if !l {
return return
} }
i := getIndex(t) ptr := getPointer(t)
increaseLockCount(i) increaseLockCount(ptr)
logLockTrack(i).Trace().Msg("locked mutex for reading") logLockTrack(ptr).Trace().Msg("locked mutex for reading")
}
// You can call this function at any time to log any dangled locks currently tracked
// it's not an error for there to be open locks, but this can help identify any
// potential issues if called judiciously
func LogDangledLocks() {
defaultLogger.Info().Msgf("Checking %v tracked locks for dangles", len(tracking))
indexMu.Lock()
var issues []struct {
ptr uintptr
entry trackingEntry
}
for ptr, entry := range tracking {
if entry.lockCount != entry.unlockCount {
issues = append(issues, struct {
ptr uintptr
entry trackingEntry
}{ptr, *entry})
}
}
indexMu.Unlock()
defaultLogger.Info().Msgf("%v potential issues", len(issues))
for _, issue := range issues {
ptr := issue.ptr
entry := issue.entry
delta := entry.lockCount - entry.unlockCount
failureType := "excess unlocks"
if delta > 0 {
failureType = "held locks"
}
defaultLogger.Warn().
Str("ptr", fmt.Sprintf("%x", ptr)).
Interface("entry", entry).
Int("delta", delta).
Msgf("dangled lock detected: %s", failureType)
}
} }

View File

@ -90,3 +90,7 @@ type Once struct {
func (o *Once) Do(f func()) { func (o *Once) Do(f func()) {
o.mu.Do(f) o.mu.Do(f)
} }
// logDangledLocks is a no-op in non-synctrace builds
func LogDangledLocks() {
}

View File

@ -4,10 +4,11 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/network/types" "github.com/jetkvm/kvm/internal/network/types"
"github.com/jetkvm/kvm/internal/sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )

View File

@ -5,9 +5,10 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/sync"
"github.com/rs/xid" "github.com/rs/xid"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )

View File

@ -6,10 +6,11 @@ import (
"context" "context"
"os" "os"
"path" "path"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/logging" "github.com/jetkvm/kvm/internal/logging"
"github.com/jetkvm/kvm/internal/sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )

View File

@ -9,9 +9,10 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )

View File

@ -6,7 +6,8 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"sync"
"github.com/jetkvm/kvm/internal/sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )

View File

@ -16,6 +16,13 @@ var appCtx context.Context
func Main() { func Main() {
logger.Log().Msg("JetKVM Starting Up") logger.Log().Msg("JetKVM Starting Up")
defer func() {
if r := recover(); r != nil {
logger.Panic().Interface("error", r).Msg("Received panic")
panic(r) // Re-panic to crash as usual
}
}()
checkFailsafeReason() checkFailsafeReason()
if failsafeModeActive { if failsafeModeActive {
logger.Warn().Str("reason", failsafeModeReason).Msg("failsafe mode activated") logger.Warn().Str("reason", failsafeModeReason).Msg("failsafe mode activated")
@ -140,6 +147,7 @@ func Main() {
<-sigs <-sigs
logger.Log().Msg("JetKVM Shutting Down") logger.Log().Msg("JetKVM Shutting Down")
//if fuseServer != nil { //if fuseServer != nil {
// err := setMassStorageImage(" ") // err := setMassStorageImage(" ")
// if err != nil { // if err != nil {

View File

@ -2,11 +2,12 @@ package kvm
import ( import (
"os" "os"
"sync"
"time" "time"
"github.com/Masterminds/semver/v3"
"github.com/jetkvm/kvm/internal/native" "github.com/jetkvm/kvm/internal/native"
"github.com/jetkvm/kvm/internal/sync"
"github.com/Masterminds/semver/v3"
"github.com/pion/webrtc/v4/pkg/media" "github.com/pion/webrtc/v4/pkg/media"
) )

View File

@ -10,16 +10,16 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"syscall" "syscall"
"time" "time"
"github.com/jetkvm/kvm/internal/sync"
"github.com/jetkvm/kvm/resource"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pion/webrtc/v4" "github.com/pion/webrtc/v4"
"github.com/psanford/httpreadat" "github.com/psanford/httpreadat"
"github.com/jetkvm/kvm/resource"
) )
func writeFile(path string, data string) error { func writeFile(path string, data string) error {

View File

@ -7,8 +7,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"sync"
"github.com/jetkvm/kvm/internal/sync"
"github.com/jetkvm/kvm/internal/websecure" "github.com/jetkvm/kvm/internal/websecure"
) )

View File

@ -6,15 +6,16 @@ import (
"encoding/json" "encoding/json"
"net" "net"
"strings" "strings"
"sync"
"time" "time"
"github.com/jetkvm/kvm/internal/hidrpc"
"github.com/jetkvm/kvm/internal/logging"
"github.com/jetkvm/kvm/internal/sync"
"github.com/jetkvm/kvm/internal/usbgadget"
"github.com/coder/websocket" "github.com/coder/websocket"
"github.com/coder/websocket/wsjson" "github.com/coder/websocket/wsjson"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/jetkvm/kvm/internal/hidrpc"
"github.com/jetkvm/kvm/internal/logging"
"github.com/jetkvm/kvm/internal/usbgadget"
"github.com/pion/webrtc/v4" "github.com/pion/webrtc/v4"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )