-
-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add battery IORegistry and sysmontap cpuusage #475
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kvs-coder do you need some help with it or why is it drafT? :-D
hey @danielpaulus it is draft because I started working on this, but then needed to switch context, and then had vacation :D I'm resuming working on this to provide the possibility to read device's stats (like Xcode does, but without beautiful visual representation, just cold numbers) If you have already some insights regarding this, I'm all ears :) |
7391a7a
to
67ba804
Compare
b94c3df
to
0260f48
Compare
3599c87
to
1ada922
Compare
72b018f
to
4430681
Compare
4430681
to
8567537
Compare
ios/dtx_codec/channel.go
Outdated
if msg.PayloadHeader.MessageType == UnknownTypeOne { | ||
d.mutex.Unlock() | ||
d.messageReceiver.msgChannel <- msg | ||
|
||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dispatcher
implementations are getting those messages already atm, and we would change that with this filter here. Lets continue to send them to the Dispatcher
, in our case now the GlobalDispatcher
.
Since GlobalDispatcher
only handles very few messages, we would need something that forwards it from there, or allow to pass in a custom Dispatcher
for the global channel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to the logs, it doesn't look like that it handles few messages, it is enormous amount of them, I tried already putting the logic in GlobalDispatcher
only :D
it results in the problem here:
1 136 129 1 137 129 1 138 129 1 139 129 1 140 129 1 141 129 1 142 129 1 143 129 1 144 129 1 145 129 1 146 129 1 147 129 1 148 129 1 149 129 1 150 129 1 151 129 1 152 129 1 153 129 1 154 129 1 155 129 1 156 129 1 157 129 1 158 129 1 159 129 1 160 129 1 161 129 1 162 129 1 163 129 1 164 129 1 165 129 1 166 129 1 167 129 1 168 129 1 169 129 1 170 129 1 171 129 1 172 129 1 173 129 1 174 129 1 175 129 1 176 129 1 177 129 1 178 129 1 179 129 1 180 129 1 181 129 1 216 3 217 3 218 3 219 3 220 3 221 3 222 3 223 3 224 3 225 3 226 3 227 3 228 3 229 3 230 3 231 3 232 3 233 3 234 3 235 3 236 3 237 3 238 3 239 3 240 3 241 3 242 3 243 3 244 3 245 3 246 3 247 3 248 3 249 3 250 3 251 3 252 3 253 3 254 3 255 4 0 4 1 4 2 4 0 0 0 0 0 0 4 2 0 0 0 0 0 0 126 189 0 0 0 0 0 0 0 0 0 0 0 0 0 2 200 208]","level":"fatal","msg":"Start call error","time":"2024-11-01T10:09:30-05:00"}
you generally receive infinite bytes stream and then ends up with the error. That is why I provided the solution as it is now in the MR :)
I checked, with this current code, it has no negative effect on our side executions as this sysmontap gets only desired message and then we close the connection and the channel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked, with this current code, it has no negative effect on our side executions as this sysmontap gets only desired message and then we close the connection and the channel
Since UnknownTypeOne
was added during the work of XCUITest for iOS 17 I strongly assume that a message with that type is sometimes sent during a test execution. And if that happens, we would block here forever as d.messageReceiver.msgChannel
is nil
. Pretty much any occasion of a message of that type would block forever, unless something calls Channel.Receive
, but that would also only help on the global channel.
That problem is solved with having it in the Dispatcher
interface as unhandled messages are only thrown away there.
We would need something in this method
go-ios/ios/dtx_codec/connection.go
Line 91 in 6fa8b80
func (g GlobalDispatcher) Dispatch(msg Message) { |
Dispatcher
(or allow to set a customer Dispatcher
on the global channel)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alright, the latest commit should safely avoid potential issues with XCTests
15f150d
to
0c0712c
Compare
0c0712c
to
58ba2d7
Compare
5de862a
to
c68ba7e
Compare
eb5b7d4
to
5785d14
Compare
5785d14
to
83b2403
Compare
) | ||
|
||
type sysmontapMsgDispatcher struct { | ||
channel chan dtx.Message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
channel chan dtx.Message | |
messages chan dtx.Message |
|
||
// Close closes up the DTX connection | ||
func (s *sysmontapService) Close() error { | ||
return s.conn.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return s.conn.Close() | |
close(s.msgDispatcher.messages) | |
return s.conn.Close() |
return SysmontapMessage{}, err | ||
} | ||
|
||
for { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you only want to read a single value here the for loop isn't needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately I really need this loop, as I previously highlighted, the global channel is like a garbage can and a lot of messages come there. The very first message is not addressing to the CPU data. If I remove the loop, the method just returns SysmontapMessage{}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah right, missed the continue
in there
case <-time.After(30 * time.Second): | ||
return SysmontapMessage{}, fmt.Errorf("exceeded waiting time message") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout is not necessary. As long as the connection is alive, we should simply wait for a value here. And by adding the close
for the channel from a comment above we can change case msg := <-s.msgDispatcher.channel:
to case msg, ok := <-s.msgDispatcher.channel:
and then the read would immediately return after the close
with ok: false
ios/instruments/system_monitor.go
Outdated
func (s *systemMonitor) GetCPUUsage() (SysmontapMessage, error) { | ||
sysAttrs, err := s.deviceInfoService.systemAttributes() | ||
if err != nil { | ||
return SysmontapMessage{}, err | ||
} | ||
|
||
procAttrs, err := s.deviceInfoService.processAttributes() | ||
if err != nil { | ||
return SysmontapMessage{}, err | ||
} | ||
|
||
err = s.sysmontapService.setConfig(procAttrs, sysAttrs) | ||
if err != nil { | ||
return SysmontapMessage{}, err | ||
} | ||
|
||
sysmontapMsg, err := s.sysmontapService.start() | ||
if err != nil { | ||
return SysmontapMessage{}, err | ||
} | ||
|
||
return sysmontapMsg, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has still the same problem as the version from last week or so. To get a single CPU sample all this setup has to be done. How do you get a second sample without doing this again? As I mentioned in the older comment, you should be able to get a continuous stream of CPU samples on a single connection, not just one.
Why not move this to NewSystemMonitor
? Without that being successful the systemMonitor
can't do anything, right? Then we can do this as part of creating it. And the call err := s.channel.MethodCallAsync("start")
should also happen there then.
} | ||
|
||
// Creates a new sysmontapService | ||
func newSysmontapService(device ios.DeviceEntry) (*sysmontapService, error) { | ||
msgDispatcher := newSysmontapMsgDispatcher() | ||
dtxConn, err := connectInstrumentsWithMsgDispatcher(device, msgDispatcher) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
processControlChannel := dtxConn.RequestChannelIdentifier(sysmontapName, loggingDispatcher{dtxConn}) | ||
|
||
return &sysmontapService{channel: processControlChannel, conn: dtxConn, msgDispatcher: msgDispatcher}, nil | ||
} | ||
|
||
// Close closes up the DTX connection | ||
func (s *sysmontapService) Close() error { | ||
return s.conn.Close() | ||
} | ||
|
||
// start sends a start method call async and waits until the cpu info & stats come back | ||
// the method is a part of the @protocol DTTapAuthorizedAPI | ||
func (s *sysmontapService) start() (SysmontapMessage, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The important part here is not that start
is getting sent (which shouldn't happen here anyways as mentioned in the comment about creating the systemMonitor
), you are reading a sample, or message. That's what should be in the name
ios/instruments/system_monitor.go
Outdated
|
||
import "github.com/danielpaulus/go-ios/ios" | ||
|
||
type systemMonitor struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for a separate type? Both, systemMonitor
and sysmontapService
contain something about CPU samples
ios/dtx_codec/connection.go
Outdated
// Dispatch prints log messages and errors when they are received and also creates local Channels when requested by the device. | ||
func (g GlobalDispatcher) Dispatch(msg Message) { | ||
SendAckIfNeeded(g.dtxConnection, msg) | ||
if msg.Payload != nil { | ||
if requestChannel == msg.Payload[0] { | ||
g.requestChannelMessages <- msg | ||
} | ||
if msg.PayloadHeader.MessageType == UnknownTypeOne { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would simply move this at the end of this method without any further checks, everything that was not handled here already gets forwarded. If the receiver of this only needs a specific type of messages, it should discard them itself
ios/dtx_codec/connection.go
Outdated
func (dtxConn *Connection) Dispatch(msg Message) { | ||
msgDispatcher := dtxConn.MessageDispatcher | ||
if msgDispatcher != nil { | ||
log.Debugf("msg dispatcher found: %v", reflect.TypeOf(msgDispatcher)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a format specifier that you can use so that you don't have to go through the reflect
library
log.Debugf("msg dispatcher found: %v", reflect.TypeOf(msgDispatcher)) | |
log.Debugf("msg dispatcher found: %T", msgDispatcher) |
6ab38d6
to
d8b2609
Compare
No description provided.