Skip to content

Commit

Permalink
add agent-bench
Browse files Browse the repository at this point in the history
  • Loading branch information
masahide committed Sep 9, 2024
1 parent 19d9c9e commit 01ed97d
Show file tree
Hide file tree
Showing 8 changed files with 571 additions and 129 deletions.
15 changes: 13 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ jobs:
run: |
wails build -nsis
go build -o build/bin/omni-socat.exe ./cmd/omni-socat
go build -o build/bin/agent-bench.exe ./cmd/agent-bench
powershell Compress-Archive -Path build/bin/omni-socat.exe -DestinationPath build/bin/omni-socat.zip
rm build/bin/omni-socat.exe
powershell Compress-Archive -Path build/bin/agent-bench.exe -DestinationPath build/bin/agent-bench.zip
rm build/bin/agent-bench.exe
powershell Compress-Archive -Path build/bin/OmniSSHAgent.exe -DestinationPath build/bin/OmniSSHAgent.zip
rm build/bin/OmniSSHAgent.exe
Expand All @@ -52,7 +55,7 @@ jobs:
run: |
echo "ls build/bin" >> $env:GITHUB_STEP_SUMMARY
ls "build/bin">> $env:GITHUB_STEP_SUMMARY
build-wsl2-ssh-agent-proxy:
build-unix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -63,7 +66,15 @@ jobs:
- name: build
run: |
CGO_ENABLED=0 go build -o build/bin/wsl2-ssh-agent-proxy ./cmd/wsl2-ssh-agent-proxy
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o build/bin/agent-bench-linux-arm64 ./cmd/agent-bench
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/bin/agent-bench-linux-amd64 ./cmd/agent-bench
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o build/bin/agent-bench-mac-arm64 ./cmd/agent-bench
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o build/bin/agent-bench-mac-amd64 ./cmd/agent-bench
gzip build/bin/wsl2-ssh-agent-proxy
gzip build/bin/agent-bench-linux-arm64
gzip build/bin/agent-bench-linux-amd64
gzip build/bin/agent-bench-mac-arm64
gzip build/bin/agent-bench-mac-amd64
- uses: actions/upload-artifact@v4
with:
name: build-files-linux
Expand All @@ -76,7 +87,7 @@ jobs:
create-release:
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-22.04
needs: [build-exe, build-wsl2-ssh-agent-proxy]
needs: [build-exe, build-unix]
steps:
- uses: actions/checkout@v4
- name: Download All Artifacts
Expand Down
165 changes: 165 additions & 0 deletions cmd/agent-bench/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package main

import (
"flag"
"fmt"
"log"
"net"
"sort"
"sync"
"time"

"github.com/kelseyhightower/envconfig"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)

type Specification struct {
PERSISTENT bool `default:"false"`
CONCURRENCY int `default:"10"`
RUN_COUNT int `default:"100"`
}

type sshAgent interface {
agent.Agent
Close() error
}
type Agent struct {
agent.ExtendedAgent
net.Conn
}

type exAgent struct {
agent.Agent
}

func (e *exAgent) SignWithFlags(key ssh.PublicKey, data []byte, flags agent.SignatureFlags) (*ssh.Signature, error) {
return nil, nil
}

func (e *exAgent) Extension(string, []byte) ([]byte, error) {
return nil, nil
}

func getKey() *agent.Key {
var key *agent.Key

a, err := newAgent()
if err != nil {
log.Fatal(err)
}
keys, err := a.List()
if err != nil {
log.Fatalf("Failed to list keys: %v", err)
}
if len(keys) == 0 {
log.Fatalf("No keys found in SSH agent")
}
key = keys[0]
a.Close()
return key
}

func main() {
s := Specification{}
err := envconfig.Process("", &s)
if err != nil {
log.Fatal(err)
}
flag.BoolVar(&s.PERSISTENT, "persistent", s.PERSISTENT, "persistent mode")
flag.IntVar(&s.CONCURRENCY, "c", s.CONCURRENCY, "Number of concurrency processing")
flag.IntVar(&s.RUN_COUNT, "n", s.RUN_COUNT, "run count")
flag.Parse()
taskCh := make(chan struct{})
doneCh := make(chan []time.Duration, s.CONCURRENCY)

var wg sync.WaitGroup
key := getKey()
fmt.Printf("The key used for measurement:%s\n", key.String())
fmt.Printf("Start %d worker\n", s.CONCURRENCY)
for i := 0; i < s.CONCURRENCY; i++ {
wg.Add(1)
go func() {
defer wg.Done()
worker(key, taskCh, doneCh, s.PERSISTENT)
}()
}

start := time.Now()
go func() {
for i := 0; i < s.RUN_COUNT; i++ {
taskCh <- struct{}{}
}
close(taskCh)
}()

go func() {
wg.Wait()
close(doneCh)
}()
var allExecutionTimes []time.Duration
for times := range doneCh {
allExecutionTimes = append(allExecutionTimes, times...)
}
fmt.Printf("\ndone.\n")
totalTime := time.Duration(0)
var minTime, maxTime time.Duration
minTime = allExecutionTimes[0]
maxTime = allExecutionTimes[0]

for _, t := range allExecutionTimes {
totalTime += t
if t < minTime {
minTime = t
}
if t > maxTime {
maxTime = t
}
}

averageTime := totalTime / time.Duration(len(allExecutionTimes))

sort.Slice(allExecutionTimes, func(i, j int) bool {
return allExecutionTimes[i] < allExecutionTimes[j]
})
p99Time := allExecutionTimes[int(float64(len(allExecutionTimes))*0.99)-1]

fmt.Printf("Real Time: %v\n", time.Since(start))
fmt.Printf("Total Executions: %d\n", s.RUN_COUNT)
fmt.Printf("Concurrency: %d\n", s.CONCURRENCY)
fmt.Printf("Persistent Mode: %v\n", s.PERSISTENT)
fmt.Printf("Total Time: %v\n", totalTime)
fmt.Printf("Average Execution Time: %v\n", averageTime)
fmt.Printf("Min Execution Time: %v\n", minTime)
fmt.Printf("Max Execution Time: %v\n", maxTime)
fmt.Printf("99th Percentile Execution Time: %v\n", p99Time)
}

func worker(key *agent.Key, taskCh <-chan struct{}, doneCh chan<- []time.Duration, persistent bool) {
var executionTimes []time.Duration
var err error
var agentClient sshAgent
for range taskCh {
start := time.Now()
if agentClient == nil {
agentClient, err = newAgent()
if err != nil {
log.Fatal(err)
}
}
data := []byte("Benchmark data")
_, err := agentClient.Sign(key, data)
if !persistent {
agentClient.Close()
agentClient = nil
}
if err != nil {
log.Printf("Failed to sign data: %v", err)
continue
}
duration := time.Since(start)
executionTimes = append(executionTimes, duration)
fmt.Print(".")
}
doneCh <- executionTimes
}
42 changes: 42 additions & 0 deletions cmd/agent-bench/unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//go:build unix

package main

import (
"log"
"net"
"os"

"golang.org/x/crypto/ssh/agent"
)

func listKeys() {
socketPath := os.Getenv("SSH_AUTH_SOCK")
conn, err := net.Dial("unix", socketPath)
if err != nil {
log.Fatal(err)
}
agentClient := agent.NewClient(conn)
list, err := agentClient.List()
if err != nil {
log.Fatal(err)
}

for _, key := range list {
log.Println(key.String())
}
}

func NewUnixDomain() (sshAgent, error) {
socketPath := os.Getenv("SSH_AUTH_SOCK")
conn, err := net.Dial("unix", socketPath)
if err != nil {
log.Fatal(err)
}
a := agent.NewClient(conn)
return &Agent{ExtendedAgent: &exAgent{a}, Conn: conn}, nil
}

func newAgent() (sshAgent, error) {
return NewUnixDomain()
}
44 changes: 44 additions & 0 deletions cmd/agent-bench/win.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//go:build windows

package main

import (
"errors"

"github.com/Microsoft/go-winio"
"github.com/davidmz/go-pageant"
"golang.org/x/crypto/ssh/agent"
)

const (
sshAgentPipe = `\\.\pipe\openssh-ssh-agent`
)

func NewPageant() (sshAgent, error) {
ok := pageant.Available()
if !ok {
return nil, errors.New("pageant is not available")
}
p := pageant.New()
return &Agent{ExtendedAgent: &exAgent{p}}, nil
}

func (a *Agent) Close() error {
if a.Conn != nil {
a.Conn.Close()
}
return nil
}

func NewNamedPipe() (sshAgent, error) {
conn, err := winio.DialPipe(sshAgentPipe, nil)
if err != nil {
return nil, err
}
a := agent.NewClient(conn)
return &Agent{ExtendedAgent: &exAgent{a}, Conn: conn}, nil
}

func newAgent() (sshAgent, error) {
return NewNamedPipe()
}
Loading

0 comments on commit 01ed97d

Please sign in to comment.