537 lines
15 KiB
PHP
537 lines
15 KiB
PHP
<?php
|
|
namespace ATF;
|
|
|
|
use Exception;
|
|
use Amp;
|
|
use taskman;
|
|
|
|
function host_exec_async(string $atf_host, string $cmd, $opts = 0, $timeout = 10) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $cmd, $opts, $timeout) {
|
|
|
|
$timeout_cmd = "timeout -k 5s $timeout $cmd";
|
|
|
|
$res = yield taskman\deploy_ssh_exec_async($atf_host, $timeout_cmd, $opts);
|
|
|
|
list($status, $lines) = current($res);
|
|
//in case of timeout we set status to false explicitely
|
|
if($status === 124)
|
|
$status = false;
|
|
|
|
return array($status, $lines);
|
|
|
|
});
|
|
}
|
|
|
|
function host_exec(string $atf_host, string $cmd, $opts = 0, $timeout = 10)
|
|
{
|
|
$timeout_cmd = "timeout -k 5s $timeout $cmd";
|
|
|
|
$res = taskman\deploy_ssh_exec($atf_host, $timeout_cmd, $opts);
|
|
|
|
list($status, $lines) = current($res);
|
|
//in case of timeout we set status to false explicitely
|
|
if($status === 124)
|
|
$status = false;
|
|
|
|
return array($status, $lines);
|
|
}
|
|
|
|
function host_get_file(string $atf_host, string $file_name)
|
|
{
|
|
$file_data = current(taskman\deploy_get_file($atf_host, $file_name));
|
|
return $file_data;
|
|
}
|
|
|
|
function host_put_file(string $atf_host, string $local_path, string $remote_path)
|
|
{
|
|
taskman\deploy_rsync($atf_host, $local_path, $remote_path);
|
|
}
|
|
|
|
function host_put_file_async(string $atf_host, string $local_path, string $remote_path)
|
|
{
|
|
return taskman\deploy_rsync_async($atf_host, $local_path, $remote_path);
|
|
}
|
|
|
|
function spread(array $items, $num)
|
|
{
|
|
$k = sizeof($items) / $num;
|
|
|
|
$res = array();
|
|
for($i=0;$i<$num;++$i)
|
|
{
|
|
$tmp = array_slice($items, (int)floor($k*$i), (int)ceil($k));
|
|
$res[$i] = $tmp;
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
function slice(array $items, $max)
|
|
{
|
|
$sliced = array();
|
|
|
|
$offset = 0;
|
|
while(true)
|
|
{
|
|
$tmp = array_slice($items, $offset, $max);
|
|
if(!$tmp)
|
|
break;
|
|
$sliced[] = $tmp;
|
|
$offset += $max;
|
|
}
|
|
|
|
return $sliced;
|
|
}
|
|
|
|
function _trim($txt, $max_len)
|
|
{
|
|
return strlen($txt) > $max_len ? substr($txt, 0, $max_len) . "..." : $txt;
|
|
}
|
|
|
|
function _trim_start($txt, $max_len)
|
|
{
|
|
return strlen($txt) > $max_len ? "..." . substr($txt, strlen($txt) - $max_len) : $txt;
|
|
}
|
|
|
|
function _trim_lines($txt, $max_lines)
|
|
{
|
|
$lines = explode("\n", $txt, $max_lines+1);
|
|
$res = '';
|
|
for($i=0;$i<sizeof($lines) && $i<$max_lines;++$i)
|
|
$res .= $lines[$i] . "\n";
|
|
return $res;
|
|
}
|
|
|
|
function _try_lz4_replay(string $txt)
|
|
{
|
|
if(!$txt)
|
|
return $txt;
|
|
//let's check if it's already archieved
|
|
if(strlen($txt) > 2 && $txt[1] === ':')
|
|
return $txt;
|
|
if(!function_exists('lz4_compress'))
|
|
return $txt;
|
|
return "4:" . base64_encode(lz4_compress(base64_decode($txt)));
|
|
}
|
|
|
|
function log($msg)
|
|
{
|
|
echo date("Y-m-d H:i:s") . " " . $msg . "\n";
|
|
}
|
|
|
|
function err($msg)
|
|
{
|
|
taskman\stderr(date("Y-m-d H:i:s") . " " . $msg . "\n");
|
|
}
|
|
|
|
function _retry($max_tries, $func)
|
|
{
|
|
for($i=0;$i<$max_tries;++$i)
|
|
{
|
|
try
|
|
{
|
|
$func();
|
|
return;
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
if(($i+1) == $max_tries)
|
|
throw $e;
|
|
}
|
|
sleep(1);
|
|
}
|
|
}
|
|
|
|
function adb_reboot(string $atf_host)
|
|
{
|
|
host_exec($atf_host, "killall adb", DEPLOY_OPT_ERR_OK);
|
|
_retry(3, function() use($atf_host) {
|
|
host_exec($atf_host, "%{adb}% kill-server");
|
|
});
|
|
host_exec($atf_host, "%{adb}% start-server");
|
|
}
|
|
|
|
function reboot_devices(string $atf_host, array $devices, int $wait_after = 40)
|
|
{
|
|
log("Rebooting devices...");
|
|
//let's reboot devices
|
|
$ces = array();
|
|
foreach($devices as $device)
|
|
{
|
|
$ces[] = Amp\call(function() use($atf_host, $device, $wait_after) {
|
|
yield host_exec_async($atf_host, "%{adb}% -s $device reboot", DEPLOY_OPT_ERR_OK);
|
|
yield Amp\delay($wait_after*1000);
|
|
});
|
|
}
|
|
Amp\Promise\wait(Amp\Promise\all($ces));
|
|
}
|
|
|
|
function get_ext_status_async(string $atf_host, string $device, int $timeout = 20) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $timeout) {
|
|
|
|
$file_data = yield device_pull_file_async($atf_host, $device, "atf_status.js", $timeout, /*throw on timeout*/true);
|
|
if($file_data === null)
|
|
return null;
|
|
$file_data = preg_replace('/[[:cntrl:]]/', '', $file_data);
|
|
return json_decode($file_data, true);
|
|
|
|
});
|
|
}
|
|
|
|
function start_ext_cmd_on_device_async(string $atf_host, string $device, Cmd $cmd, array $cmd_args) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $cmd, $cmd_args) {
|
|
|
|
yield device_del_file_async($atf_host, $device, 'atf_status.js', DEPLOY_OPT_ERR_OK);
|
|
yield host_exec_async($atf_host, "%{adb}% -s $device shell am force-stop %{package_id}%", DEPLOY_OPT_ERR_OK, 30);
|
|
yield update_ext_cmd_async($atf_host, $device, $cmd, $cmd_args);
|
|
yield host_exec_async($atf_host, "%{adb}% -s $device shell am start -n %{package_id}%/%{activity}%", 0, 30);
|
|
|
|
});
|
|
}
|
|
|
|
function get_devices(string $atf_host, bool $extended = false)
|
|
{
|
|
$devices = array();
|
|
|
|
list($_, $lines) = host_exec($atf_host, "%{adb}% devices -l", DEPLOY_OPT_SILENT);
|
|
|
|
foreach($lines as $line)
|
|
{
|
|
if(preg_match('~^(\S+)\s+device(.*)~', $line, $m))
|
|
{
|
|
if($extended)
|
|
{
|
|
$devices[$m[1]] = array();
|
|
$items = explode(" ", trim($m[2]));
|
|
foreach($items as $item)
|
|
{
|
|
list($k, $v) = explode(":", $item);
|
|
$devices[$m[1]][$k] = $v;
|
|
}
|
|
}
|
|
else
|
|
$devices[] = $m[1];
|
|
}
|
|
}
|
|
return $devices;
|
|
}
|
|
|
|
function update_ext_cmd_async(string $atf_host, string $device, Cmd $cmd, array $args)
|
|
{
|
|
$ext_cmd = array(
|
|
"device_id" => $device,
|
|
"module" => $cmd->module,
|
|
"func" => $cmd->func,
|
|
"args" => $args,
|
|
);
|
|
|
|
return device_put_file_async($atf_host, $device, json_encode($ext_cmd), 'atf_cmd.js');
|
|
}
|
|
|
|
function package_dir(string $atf_host)
|
|
{
|
|
return "/storage/emulated/0/Android/data/".taskman\deploy_get($atf_host, 'package_id')."/files/";
|
|
}
|
|
|
|
function device_put_file_async(string $atf_host, string $device, string $data_or_file, string $device_path, bool $is_file = false, $timeout = 30) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $data_or_file, $device_path, $is_file, $timeout) {
|
|
|
|
if($device_path[0] !== "/")
|
|
$device_path = package_dir($atf_host) . $device_path;
|
|
|
|
$tmp_remote_file = '%{dir}%'.uniqid(basename($device_path)."_");
|
|
|
|
if(!$is_file)
|
|
{
|
|
$local_path = tempnam("/tmp", "atf_");
|
|
taskman\ensure_write($local_path, $data_or_file);
|
|
}
|
|
else
|
|
{
|
|
$local_path = $data_or_file;
|
|
if(!is_file($local_path))
|
|
throw new Exception("No such local file: $local_path");
|
|
}
|
|
|
|
try
|
|
{
|
|
//1. let's copy local file to the ATF host as temp one
|
|
yield host_put_file_async($atf_host, $local_path, $tmp_remote_file);
|
|
}
|
|
finally
|
|
{
|
|
if(!$is_file)
|
|
taskman\ensure_rm($local_path);
|
|
}
|
|
|
|
//2. let's push the temp file to the device
|
|
$device_dir = dirname($device_path);
|
|
yield host_exec_async($atf_host, "%{adb}% -s $device shell mkdir -p $device_dir");
|
|
yield host_exec_async($atf_host, "%{adb}% -s $device push $tmp_remote_file $device_path", 0, $timeout);
|
|
yield host_exec_async($atf_host, "%{adb}% -s $device shell chmod -R 777 $device_dir", DEPLOY_OPT_ERR_OK);
|
|
yield host_exec_async($atf_host, "rm -rf $tmp_remote_file");
|
|
|
|
});
|
|
}
|
|
|
|
function device_del_file(string $atf_host, string $device, string $device_path, $opts = 0)
|
|
{
|
|
if($device_path[0] !== "/")
|
|
$device_path = package_dir($atf_host) . $device_path;
|
|
|
|
host_exec($atf_host, "%{adb}% -s $device shell rm -rf $device_path", $opts);
|
|
}
|
|
|
|
function device_del_file_async(string $atf_host, string $device, string $device_path, $opts = 0)
|
|
{
|
|
if($device_path[0] !== "/")
|
|
$device_path = package_dir($atf_host) . $device_path;
|
|
|
|
return host_exec_async($atf_host, "%{adb}% -s $device shell rm -rf $device_path", $opts);
|
|
}
|
|
|
|
function device_blink($atf_host, string $device, int $times = 5)
|
|
{
|
|
//disable auto brightness
|
|
host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness_mode 0");
|
|
|
|
for($i=0;$i<$times;++$i)
|
|
{
|
|
host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness 255");
|
|
sleep(1);
|
|
host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness 5");
|
|
}
|
|
|
|
//enable auto brightness
|
|
host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness 100");
|
|
host_exec($atf_host, "%{adb}% -s $device shell settings put system screen_brightness_mode 1");
|
|
}
|
|
|
|
function device_put_dir_async(string $atf_host, string $device, string $folder_path, string $device_parent_path, int $timeout = 60) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $folder_path, $device_parent_path, $timeout) {
|
|
|
|
if($device_parent_path[0] !== "/")
|
|
$device_parent_path = package_dir($atf_host) . $device_parent_path;
|
|
|
|
$remote_path = '/tmp/' . basename($folder_path);
|
|
|
|
yield taskman\deploy_rsync_async($atf_host, "$folder_path/", "$remote_path/", '--delete');
|
|
|
|
yield host_exec_async($atf_host, "%{adb}% -s $device push --sync $remote_path $device_parent_path", 0, $timeout);
|
|
|
|
});
|
|
}
|
|
|
|
function device_pull_file_async(string $atf_host, string $device, string $device_path, int $timeout = 10, bool $throw_error_on_timeout = false) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $device_path, $throw_error_on_timeout, $timeout) {
|
|
|
|
if($device_path[0] !== "/")
|
|
$device_path = package_dir($atf_host) . $device_path;
|
|
|
|
$tmp_remote_file = '%{dir}%/'.uniqid(basename($device_path)."_");
|
|
|
|
list($status, $_) = yield host_exec_async($atf_host, "%{adb}% -s $device pull $device_path $tmp_remote_file", DEPLOY_OPT_ERR_OK, $timeout);
|
|
|
|
if($status !== 0)
|
|
{
|
|
if($status === false && $throw_error_on_timeout)
|
|
throw new Exception("Could not pull from device '$device' file '$device_path' due to timeout");
|
|
return null;
|
|
}
|
|
|
|
$file_data = host_get_file($atf_host, $tmp_remote_file);
|
|
yield host_exec_async($atf_host, "rm -rf $tmp_remote_file");
|
|
return $file_data;
|
|
|
|
});
|
|
}
|
|
|
|
function get_last_replay_async(string $atf_host, string $device, string $device_replay_path) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $device_replay_path) {
|
|
$file = package_dir($atf_host) . $device_replay_path;
|
|
list($status, $contents) = yield host_exec_async($atf_host, "%{adb}% -s $device shell cat $file", DEPLOY_OPT_ERR_OK);
|
|
return array($status, implode("\n", $contents));
|
|
});
|
|
}
|
|
|
|
function _multi_curl_async($ch, $get_content) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($ch, $get_content) {
|
|
$mh = curl_multi_init();
|
|
curl_multi_add_handle($mh, $ch);
|
|
|
|
do {
|
|
$status = curl_multi_exec($mh, $active);
|
|
yield Amp\delay(1);
|
|
} while ($active && $status == CURLM_OK);
|
|
|
|
$result = null;
|
|
if($get_content)
|
|
$result = curl_multi_getcontent($ch);
|
|
|
|
curl_multi_remove_handle($mh, $ch);
|
|
curl_multi_close($mh);
|
|
|
|
return $result;
|
|
});
|
|
}
|
|
|
|
function device_screen(string $atf_host, string $device)
|
|
{
|
|
$screen_file_name = '%{dir}%/'.uniqid("screen_").'.png';
|
|
try
|
|
{
|
|
host_exec($atf_host, "%{adb}% -s $device exec-out screencap -p > $screen_file_name");
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
return false;
|
|
}
|
|
$data = host_get_file($atf_host, $screen_file_name);
|
|
host_exec($atf_host, "rm -rf $screen_file_name");
|
|
return $data;
|
|
}
|
|
|
|
function del_external_files_async(string $atf_host, string $device, array $external_files) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $external_files) {
|
|
|
|
foreach($external_files as $real_path => $remote_path)
|
|
yield device_del_file_async($atf_host, $device, $remote_path);
|
|
|
|
});
|
|
}
|
|
|
|
function put_external_files_async(string $atf_host, string $device, array $external_files) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device, $external_files) {
|
|
|
|
foreach($external_files as $real_path => $remote_path)
|
|
yield device_put_file_async($atf_host, $device, $real_path, $remote_path, true);
|
|
|
|
});
|
|
}
|
|
|
|
function get_locat_errors_since(string $atf_host, string $device, int $since_stamp)
|
|
{
|
|
$time_spec = gmdate('m-d h:m:s.0', $since_stamp);
|
|
list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -v UTC -d -t '$time_spec' AndroidRuntime:E Unity:E", DEPLOY_OPT_ERR_OK);
|
|
_filter_error_logs($lines);
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
function get_logcat_errors(string $atf_host, string $device, int $limit = 0)
|
|
{
|
|
list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -d ".($limit > 0 ? "-t $limit" : "")." AndroidRuntime:E Unity:E", DEPLOY_OPT_ERR_OK);
|
|
_filter_error_logs($lines);
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
function get_locat_unity_since(string $atf_host, string $device, int $since_stamp)
|
|
{
|
|
$time_spec = gmdate('m-d h:m:s.0', $since_stamp);
|
|
list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -v UTC -d -t '$time_spec' Unity:'*'", DEPLOY_OPT_ERR_OK);
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
function get_logcat_unity(string $atf_host, string $device, int $limit = 0)
|
|
{
|
|
list($_, $lines) = host_exec($atf_host, "%{adb}% -s $device logcat -s -d Unity:'*'", DEPLOY_OPT_ERR_OK);
|
|
if($limit > 0)
|
|
array_splice($lines, 0, sizeof($lines) - $limit);
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
function _filter_error_logs(array &$lines)
|
|
{
|
|
$exception_lines = 0;
|
|
$filtered = array();
|
|
for($i=0;$i<sizeof($lines);++$i)
|
|
{
|
|
$line = $lines[$i];
|
|
if(strpos($line, 'E AndroidRuntime') !== false)
|
|
$filtered[] = $line;
|
|
else if(strpos($line, 'E Unity') !== false)
|
|
{
|
|
if(strpos($line, '[EXCEPTION]') !== false)
|
|
$exception_lines = 15;
|
|
|
|
if(--$exception_lines > 0)
|
|
$filtered[] = $line;
|
|
}
|
|
}
|
|
$lines = $filtered;
|
|
}
|
|
|
|
function device_pss_async(string $atf_host, string $device) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device) {
|
|
$res = yield device_mem_async($atf_host, $device);
|
|
return $res['total'];
|
|
});
|
|
}
|
|
|
|
function device_mem_async(string $atf_host, string $device) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device) {
|
|
list($code, $lines) = yield host_exec_async($atf_host, "%{adb}% -s $device shell dumpsys meminfo -s %{package_id}%", DEPLOY_OPT_ERR_OK, 20);
|
|
|
|
$res = array(
|
|
'total' => 0,
|
|
'native' => 0,
|
|
'java' => 0,
|
|
'system' => 0,
|
|
'graphics' => 0,
|
|
);
|
|
|
|
if($code !== 0)
|
|
return $res;
|
|
|
|
foreach($lines as $idx => $line)
|
|
{
|
|
$items = preg_split('/\s+/', trim($line));
|
|
if($items && $items[0] === "TOTAL:")
|
|
$res['total'] = intval($items[1]);
|
|
else if($items && $items[0] === "TOTAL" && $items[1] === "PSS:")
|
|
$res['total'] = intval($items[2]);
|
|
else if($items && $items[0] === "Native" && $items[1] === "Heap:")
|
|
$res['native'] = intval($items[2]);
|
|
else if($items && $items[0] === "Java" && $items[1] === "Heap:")
|
|
$res['java'] = intval($items[2]);
|
|
else if($items && $items[0] === "System:")
|
|
$res['system'] = intval($items[1]);
|
|
else if($items && $items[0] === "Graphics:")
|
|
$res['graphics'] = intval($items[1]);
|
|
}
|
|
|
|
return $res;
|
|
});
|
|
}
|
|
|
|
function device_temperature_async(string $atf_host, string $device) : Amp\Promise
|
|
{
|
|
return Amp\call(function() use($atf_host, $device) {
|
|
//NOTE: on current devices 16 thermal zone is responsible for GPU temperature probing
|
|
list($code, $lines) = yield host_exec_async($atf_host, "%{adb}% -s $device shell cat /sys/class/thermal/thermal_zone16/temp", DEPLOY_OPT_ERR_OK, 1);
|
|
|
|
if($code !== 0)
|
|
return 0;
|
|
|
|
foreach($lines as $idx => $line)
|
|
{
|
|
$line = trim($line);
|
|
if(!$line)
|
|
continue;
|
|
return intval($line);
|
|
}
|
|
|
|
return 0;
|
|
});
|
|
}
|