Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 109 additions & 7 deletions device.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import (
"strings"
"time"

uuid "github.com/satori/go.uuid"
"howett.net/plist"

"github.com/electricbubble/gidevice/pkg/ipa"
"github.com/electricbubble/gidevice/pkg/libimobiledevice"
"github.com/electricbubble/gidevice/pkg/nskeyedarchiver"
uuid "github.com/satori/go.uuid"
"howett.net/plist"
)

const LockdownPort = 62078
Expand Down Expand Up @@ -47,6 +48,7 @@ type device struct {
springBoard SpringBoard
crashReportMover CrashReportMover
pcapd Pcapd
perfd []Perfd
}

func (d *device) Properties() DeviceProperties {
Expand Down Expand Up @@ -315,14 +317,19 @@ func (d *device) InstallationProxyLookup(opts ...InstallationProxyOption) (looku
return d.installationProxy.Lookup(opts...)
}

func (d *device) newInstrumentsService() (instruments Instruments, err error) {
// NOTICE: each instruments service should have individual connection, otherwise it will be blocked
if _, err = d.lockdownService(); err != nil {
return
}
return d.lockdown.InstrumentsService()
}

func (d *device) instrumentsService() (instruments Instruments, err error) {
if d.instruments != nil {
return d.instruments, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.instruments, err = d.lockdown.InstrumentsService(); err != nil {
if d.instruments, err = d.newInstrumentsService(); err != nil {
return nil, err
}
instruments = d.instruments
Expand Down Expand Up @@ -570,6 +577,102 @@ func (d *device) PcapStop() {
d.pcapd.Stop()
}

func (d *device) PerfStart(opts ...PerfOption) (data <-chan []byte, err error) {
perfOptions := defaulPerfOption()
for _, fn := range opts {
fn(perfOptions)
}

// wait until get pid for bundle id
if perfOptions.BundleID != "" {
instruments, err := d.newInstrumentsService()
if err != nil {
fmt.Printf("get pid by bundle id failed: %v\n", err)
os.Exit(1)
}

for {
pid, err := instruments.getPidByBundleID(perfOptions.BundleID)
if err != nil {
time.Sleep(1 * time.Second)
continue
}
perfOptions.Pid = pid
break
}
}

// processAttributes must contain pid, or it can't get process info, reason unknown
if !containString(perfOptions.ProcessAttributes, "pid") {
perfOptions.ProcessAttributes = append(perfOptions.ProcessAttributes, "pid")
}

outCh := make(chan []byte, 100)

if perfOptions.SysCPU || perfOptions.SysMem || perfOptions.SysDisk ||
perfOptions.SysNetwork {
perfd, err := d.newPerfdSysmontap(perfOptions)
if err != nil {
return nil, err
}
data, err := perfd.Start()
if err != nil {
return nil, err
}
go func() {
for {
outCh <- (<-data)
}
}()
d.perfd = append(d.perfd, perfd)
}

if perfOptions.Network {
perfd, err := d.newPerfdNetworking(perfOptions)
if err != nil {
return nil, err
}
data, err := perfd.Start()
if err != nil {
return nil, err
}
go func() {
for {
outCh <- (<-data)
}
}()
d.perfd = append(d.perfd, perfd)
}

if perfOptions.FPS || perfOptions.gpu {
perfd, err := d.newPerfdGraphicsOpengl(perfOptions)
if err != nil {
return nil, err
}
data, err := perfd.Start()
if err != nil {
return nil, err
}
go func() {
for {
outCh <- (<-data)
}
}()
d.perfd = append(d.perfd, perfd)
}

return outCh, nil
}

func (d *device) PerfStop() {
if d.perfd == nil {
return
}
for _, p := range d.perfd {
p.Stop()
}
}

func (d *device) crashReportMoverService() (crashReportMover CrashReportMover, err error) {
if d.crashReportMover != nil {
return d.crashReportMover, nil
Expand Down Expand Up @@ -783,7 +886,6 @@ func (d *device) XCTest(bundleID string, opts ...XCTestOption) (out <-chan strin
}
// time.Sleep(time.Second)
close(_out)
return
}()

return _out, cancelFunc, err
Expand Down
13 changes: 11 additions & 2 deletions idevice.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"context"
"fmt"
"log"
"time"

"github.com/electricbubble/gidevice/pkg/libimobiledevice"
Expand Down Expand Up @@ -77,6 +76,9 @@ type Device interface {
springBoardService() (springBoard SpringBoard, err error)
GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error)
GetInterfaceOrientation() (orientation OrientationState, err error)

PerfStart(opts ...PerfOption) (data <-chan []byte, err error)
PerfStop()
}

type DeviceProperties = libimobiledevice.DeviceProperties
Expand Down Expand Up @@ -143,11 +145,13 @@ type Instruments interface {
AppList(opts ...AppListOption) (apps []Application, err error)
DeviceInfo() (devInfo *DeviceInfo, err error)

getPidByBundleID(bundleID string) (pid int, err error)
appProcess(bundleID string) (err error)
startObserving(pid int) (err error)

notifyOfPublishedCapabilities() (err error)
requestChannel(channel string) (id uint32, err error)
call(channel, selector string, auxiliaries ...interface{}) (result *libimobiledevice.DTXMessageResult, err error)

// sysMonSetConfig(cfg ...interface{}) (err error)
// SysMonStart(cfg ...interface{}) (_ interface{}, err error)
Expand Down Expand Up @@ -217,6 +221,11 @@ type Pcapd interface {
Stop()
}

type Perfd interface {
Start() (data <-chan []byte, err error)
Stop()
}

type DiagnosticsRelay interface {
Reboot() error
Shutdown() error
Expand Down Expand Up @@ -469,5 +478,5 @@ func debugLog(msg string) {
if !debugFlag {
return
}
log.Println(fmt.Sprintf("[go-iDevice-debug] %s", msg))
fmt.Printf("[go-iDevice-debug] %s\n", msg)
}
78 changes: 71 additions & 7 deletions instruments.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,26 @@ package giDevice
import (
"encoding/json"
"fmt"

"github.com/electricbubble/gidevice/pkg/libimobiledevice"
)

// instruments services
const (
instrumentsServiceDeviceInfo = "com.apple.instruments.server.services.deviceinfo"
instrumentsServiceProcessControl = "com.apple.instruments.server.services.processcontrol"
instrumentsServiceDeviceApplictionListing = "com.apple.instruments.server.services.device.applictionListing"
instrumentsServiceGraphicsOpengl = "com.apple.instruments.server.services.graphics.opengl" // 获取 GPU/FPS
instrumentsServiceSysmontap = "com.apple.instruments.server.services.sysmontap" // 获取 CPU/Mem/Disk/Network 性能数据
instrumentsServiceNetworking = "com.apple.instruments.server.services.networking" // 获取所有网络详情数据
instrumentsServiceMobileNotifications = "com.apple.instruments.server.services.mobilenotifications" // 监控应用状态
)

const (
instrumentsServiceXcodeNetworkStatistics = "com.apple.xcode.debug-gauge-data-providers.NetworkStatistics" // 获取单进程网络数据
instrumentsServiceXcodeEnergyStatistics = "com.apple.xcode.debug-gauge-data-providers.Energy" // 获取功耗数据
)

var _ Instruments = (*instruments)(nil)

func newInstruments(client *libimobiledevice.InstrumentsClient) *instruments {
Expand All @@ -27,6 +44,53 @@ func (i *instruments) requestChannel(channel string) (id uint32, err error) {
return i.client.RequestChannel(channel)
}

func (i *instruments) call(channel, selector string, auxiliaries ...interface{}) (
result *libimobiledevice.DTXMessageResult, err error) {

chanID, err := i.requestChannel(channel)
if err != nil {
return nil, err
}

args := libimobiledevice.NewAuxBuffer()
for _, aux := range auxiliaries {
if err = args.AppendObject(aux); err != nil {
return nil, err
}
}

return i.client.Invoke(selector, args, chanID, true)
}

func (i *instruments) getPidByBundleID(bundleID string) (pid int, err error) {
apps, err := i.AppList()
if err != nil {
fmt.Printf("get app list error: %v\n", err)
return 0, err
}

mapper := make(map[string]interface{})
for _, app := range apps {
mapper[app.ExecutableName] = app.CFBundleIdentifier
}

processes, err := i.AppRunningProcesses()
if err != nil {
fmt.Printf("get running app processes error: %v\n", err)
return 0, err
}
for _, proc := range processes {
b, ok := mapper[proc.Name]
if ok && bundleID == b {
fmt.Printf("get pid %d by bundleId %s\n", proc.Pid, bundleID)
return proc.Pid, nil
}
}

fmt.Printf("can't find pid by bundleID: %s\n", bundleID)
return 0, fmt.Errorf("can't find pid by bundleID: %s", bundleID)
}

func (i *instruments) AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error) {
opt := new(appLaunchOption)
opt.appPath = ""
Expand All @@ -41,7 +105,7 @@ func (i *instruments) AppLaunch(bundleID string, opts ...AppLaunchOption) (pid i
}

var id uint32
if id, err = i.requestChannel("com.apple.instruments.server.services.processcontrol"); err != nil {
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return 0, err
}

Expand Down Expand Up @@ -77,7 +141,7 @@ func (i *instruments) AppLaunch(bundleID string, opts ...AppLaunchOption) (pid i

func (i *instruments) appProcess(bundleID string) (err error) {
var id uint32
if id, err = i.requestChannel("com.apple.instruments.server.services.processcontrol"); err != nil {
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return err
}

Expand All @@ -96,7 +160,7 @@ func (i *instruments) appProcess(bundleID string) (err error) {

func (i *instruments) startObserving(pid int) (err error) {
var id uint32
if id, err = i.requestChannel("com.apple.instruments.server.services.processcontrol"); err != nil {
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return err
}

Expand All @@ -119,7 +183,7 @@ func (i *instruments) startObserving(pid int) (err error) {

func (i *instruments) AppKill(pid int) (err error) {
var id uint32
if id, err = i.requestChannel("com.apple.instruments.server.services.processcontrol"); err != nil {
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return err
}

Expand All @@ -138,7 +202,7 @@ func (i *instruments) AppKill(pid int) (err error) {

func (i *instruments) AppRunningProcesses() (processes []Process, err error) {
var id uint32
if id, err = i.requestChannel("com.apple.instruments.server.services.deviceinfo"); err != nil {
if id, err = i.requestChannel(instrumentsServiceDeviceInfo); err != nil {
return nil, err
}

Expand Down Expand Up @@ -187,7 +251,7 @@ func (i *instruments) AppList(opts ...AppListOption) (apps []Application, err er
}

var id uint32
if id, err = i.requestChannel("com.apple.instruments.server.services.device.applictionListing"); err != nil {
if id, err = i.requestChannel(instrumentsServiceDeviceApplictionListing); err != nil {
return nil, err
}

Expand Down Expand Up @@ -232,7 +296,7 @@ func (i *instruments) AppList(opts ...AppListOption) (apps []Application, err er

func (i *instruments) DeviceInfo() (devInfo *DeviceInfo, err error) {
var id uint32
if id, err = i.requestChannel("com.apple.instruments.server.services.deviceinfo"); err != nil {
if id, err = i.requestChannel(instrumentsServiceDeviceInfo); err != nil {
return nil, err
}

Expand Down
Loading