diff options
author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2024-11-20 16:54:44 +0000 |
---|---|---|
committer | Steve Sakoman <steve@sakoman.com> | 2025-01-13 05:49:08 -0800 |
commit | 66f9cec85308921d62fa447f10828043c89a79c2 (patch) | |
tree | 4b90862ae78edb1a461a5ae6598bfe220f45f844 | |
parent | f7169136671458a98690a7eb3328aa743f02d19e (diff) | |
download | poky-66f9cec85308921d62fa447f10828043c89a79c2.tar.gz |
oeqa/ssh: Improve performance and log sizes
The current code is not fit for purpose when handling large files via ssh. In the strace
ptest case, we can end up with a 1.4GB archive being transferred for which every
byte is printed into the task logfile twice over. This is then sent over bitbake IPC
which compounds the problems.
Make the following improvements:
* when the output is large (over 64kb), don't print it
* use a bytearray for better concat performance since strings are slow for this
* when there is no ssh output, say that
* print periodic size status output rather than the data itself since this could be binary and/or large
* fix the killed process message logic which appeared broken
(From OE-Core rev: 57673a71b20e2bbb53e7652a709bdcb32c429b6b)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit e7dd009a17dc902852983a82bce41bf78bb1e242)
Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r-- | meta/lib/oeqa/core/target/ssh.py | 44 |
1 files changed, 32 insertions, 12 deletions
diff --git a/meta/lib/oeqa/core/target/ssh.py b/meta/lib/oeqa/core/target/ssh.py index d473469384..8b5c450a05 100644 --- a/meta/lib/oeqa/core/target/ssh.py +++ b/meta/lib/oeqa/core/target/ssh.py | |||
@@ -92,7 +92,10 @@ class OESSHTarget(OETarget): | |||
92 | processTimeout = self.timeout | 92 | processTimeout = self.timeout |
93 | 93 | ||
94 | status, output = self._run(sshCmd, processTimeout, ignore_status, raw) | 94 | status, output = self._run(sshCmd, processTimeout, ignore_status, raw) |
95 | self.logger.debug('Command: %s\nStatus: %d Output: %s\n' % (command, status, output)) | 95 | if len(output) > (64 * 1024): |
96 | self.logger.debug('Command: %s\nStatus: %d Output length: %s\n' % (command, status, len(output))) | ||
97 | else: | ||
98 | self.logger.debug('Command: %s\nStatus: %d Output: %s\n' % (command, status, output)) | ||
96 | 99 | ||
97 | return (status, output) | 100 | return (status, output) |
98 | 101 | ||
@@ -211,17 +214,18 @@ def SSHCall(command, logger, timeout=None, raw=False, **opts): | |||
211 | def run(): | 214 | def run(): |
212 | nonlocal output | 215 | nonlocal output |
213 | nonlocal process | 216 | nonlocal process |
214 | output_raw = b'' | 217 | output_raw = bytearray() |
215 | starttime = time.time() | 218 | starttime = time.time() |
219 | progress = time.time() | ||
216 | process = subprocess.Popen(command, **options) | 220 | process = subprocess.Popen(command, **options) |
217 | has_timeout = False | 221 | has_timeout = False |
222 | appendline = None | ||
218 | if timeout: | 223 | if timeout: |
219 | endtime = starttime + timeout | 224 | endtime = starttime + timeout |
220 | eof = False | 225 | eof = False |
221 | os.set_blocking(process.stdout.fileno(), False) | 226 | os.set_blocking(process.stdout.fileno(), False) |
222 | while not has_timeout and not eof: | 227 | while not has_timeout and not eof: |
223 | try: | 228 | try: |
224 | logger.debug('Waiting for process output: time: %s, endtime: %s' % (time.time(), endtime)) | ||
225 | if select.select([process.stdout], [], [], 5)[0] != []: | 229 | if select.select([process.stdout], [], [], 5)[0] != []: |
226 | # wait a bit for more data, tries to avoid reading single characters | 230 | # wait a bit for more data, tries to avoid reading single characters |
227 | time.sleep(0.2) | 231 | time.sleep(0.2) |
@@ -229,9 +233,9 @@ def SSHCall(command, logger, timeout=None, raw=False, **opts): | |||
229 | if not data: | 233 | if not data: |
230 | eof = True | 234 | eof = True |
231 | else: | 235 | else: |
232 | output_raw += data | 236 | output_raw.extend(data) |
233 | # ignore errors to capture as much as possible | 237 | # ignore errors to capture as much as possible |
234 | logger.debug('Partial data from SSH call:\n%s' % data.decode('utf-8', errors='ignore')) | 238 | #logger.debug('Partial data from SSH call:\n%s' % data.decode('utf-8', errors='ignore')) |
235 | endtime = time.time() + timeout | 239 | endtime = time.time() + timeout |
236 | except InterruptedError: | 240 | except InterruptedError: |
237 | logger.debug('InterruptedError') | 241 | logger.debug('InterruptedError') |
@@ -244,6 +248,10 @@ def SSHCall(command, logger, timeout=None, raw=False, **opts): | |||
244 | logger.debug('SSHCall has timeout! Time: %s, endtime: %s' % (time.time(), endtime)) | 248 | logger.debug('SSHCall has timeout! Time: %s, endtime: %s' % (time.time(), endtime)) |
245 | has_timeout = True | 249 | has_timeout = True |
246 | 250 | ||
251 | if time.time() >= (progress + 60): | ||
252 | logger.debug('Waiting for process output at time: %s with datasize: %s' % (time.time(), len(output_raw))) | ||
253 | progress = time.time() | ||
254 | |||
247 | process.stdout.close() | 255 | process.stdout.close() |
248 | 256 | ||
249 | # process hasn't returned yet | 257 | # process hasn't returned yet |
@@ -256,17 +264,29 @@ def SSHCall(command, logger, timeout=None, raw=False, **opts): | |||
256 | logger.debug('OSError when killing process') | 264 | logger.debug('OSError when killing process') |
257 | pass | 265 | pass |
258 | endtime = time.time() - starttime | 266 | endtime = time.time() - starttime |
259 | lastline = ("\nProcess killed - no output for %d seconds. Total" | 267 | appendline = ("\nProcess killed - no output for %d seconds. Total" |
260 | " running time: %d seconds." % (timeout, endtime)) | 268 | " running time: %d seconds." % (timeout, endtime)) |
261 | logger.debug('Received data from SSH call:\n%s ' % lastline) | 269 | logger.debug('Received data from SSH call:\n%s ' % appendline) |
262 | output += lastline | ||
263 | process.wait() | 270 | process.wait() |
264 | 271 | ||
272 | if raw: | ||
273 | output = bytes(output_raw) | ||
274 | if appendline: | ||
275 | output += bytes(appendline, "utf-8") | ||
276 | else: | ||
277 | output = output_raw.decode('utf-8', errors='ignore') | ||
278 | if appendline: | ||
279 | output += appendline | ||
265 | else: | 280 | else: |
266 | output_raw = process.communicate()[0] | 281 | output = output_raw = process.communicate()[0] |
267 | 282 | if not raw: | |
268 | output = output_raw if raw else output_raw.decode('utf-8', errors='ignore') | 283 | output = output_raw.decode('utf-8', errors='ignore') |
269 | logger.debug('Data from SSH call:\n%s' % output.rstrip()) | 284 | |
285 | if len(output) < (64 * 1024): | ||
286 | if output.rstrip(): | ||
287 | logger.debug('Data from SSH call:\n%s' % output.rstrip()) | ||
288 | else: | ||
289 | logger.debug('No output from SSH call') | ||
270 | 290 | ||
271 | # timout or not, make sure process exits and is not hanging | 291 | # timout or not, make sure process exits and is not hanging |
272 | if process.returncode == None: | 292 | if process.returncode == None: |