mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-05-22 21:44:39 +00:00
198 lines
5.2 KiB
PHP
198 lines
5.2 KiB
PHP
<?php
|
|
|
|
/*
|
|
*
|
|
* ____ _ _ __ __ _ __ __ ____
|
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* @author PocketMine Team
|
|
* @link http://www.pocketmine.net/
|
|
*
|
|
*
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace pocketmine\utils;
|
|
|
|
use pocketmine\thread\ThreadManager;
|
|
use function count;
|
|
use function exec;
|
|
use function fclose;
|
|
use function file;
|
|
use function file_get_contents;
|
|
use function function_exists;
|
|
use function getmypid;
|
|
use function getmyuid;
|
|
use function hexdec;
|
|
use function memory_get_usage;
|
|
use function posix_kill;
|
|
use function preg_match;
|
|
use function proc_close;
|
|
use function proc_open;
|
|
use function stream_get_contents;
|
|
use function strpos;
|
|
use function trim;
|
|
|
|
final class Process{
|
|
|
|
private function __construct(){
|
|
//NOOP
|
|
}
|
|
|
|
/**
|
|
* @return int[]
|
|
* @phpstan-return array{int,int,int}
|
|
*/
|
|
public static function getAdvancedMemoryUsage(){
|
|
$reserved = memory_get_usage();
|
|
$VmSize = null;
|
|
$VmRSS = null;
|
|
if(Utils::getOS() === Utils::OS_LINUX || Utils::getOS() === Utils::OS_ANDROID){
|
|
$status = @file_get_contents("/proc/self/status");
|
|
if($status === false) throw new AssumptionFailedError("/proc/self/status should always be accessible");
|
|
|
|
// the numbers found here should never be bigger than PHP_INT_MAX, so we expect them to always be castable to int
|
|
if(preg_match("/VmRSS:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
|
$VmRSS = ((int) $matches[1]) * 1024;
|
|
}
|
|
|
|
if(preg_match("/VmSize:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
|
$VmSize = ((int) $matches[1]) * 1024;
|
|
}
|
|
}
|
|
|
|
//TODO: more OS
|
|
|
|
if($VmRSS === null){
|
|
$VmRSS = memory_get_usage();
|
|
}
|
|
|
|
if($VmSize === null){
|
|
$VmSize = memory_get_usage(true);
|
|
}
|
|
|
|
return [$reserved, $VmRSS, $VmSize];
|
|
}
|
|
|
|
public static function getMemoryUsage() : int{
|
|
return self::getAdvancedMemoryUsage()[1];
|
|
}
|
|
|
|
/**
|
|
* @return int[]
|
|
*/
|
|
public static function getRealMemoryUsage() : array{
|
|
$stack = 0;
|
|
$heap = 0;
|
|
|
|
if(Utils::getOS() === Utils::OS_LINUX || Utils::getOS() === Utils::OS_ANDROID){
|
|
$mappings = @file("/proc/self/maps");
|
|
if($mappings === false) throw new AssumptionFailedError("/proc/self/maps should always be accessible");
|
|
foreach($mappings as $line){
|
|
if(preg_match("#([a-z0-9]+)\\-([a-z0-9]+) [rwxp\\-]{4} [a-z0-9]+ [^\\[]*\\[([a-zA-z0-9]+)\\]#", trim($line), $matches) > 0){
|
|
if(strpos($matches[3], "heap") === 0){
|
|
$heap += (int) hexdec($matches[2]) - (int) hexdec($matches[1]);
|
|
}elseif(strpos($matches[3], "stack") === 0){
|
|
$stack += (int) hexdec($matches[2]) - (int) hexdec($matches[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return [$heap, $stack];
|
|
}
|
|
|
|
public static function getThreadCount() : int{
|
|
if(Utils::getOS() === Utils::OS_LINUX || Utils::getOS() === Utils::OS_ANDROID){
|
|
$status = @file_get_contents("/proc/self/status");
|
|
if($status === false) throw new AssumptionFailedError("/proc/self/status should always be accessible");
|
|
if(preg_match("/Threads:[ \t]+([0-9]+)/", $status, $matches) > 0){
|
|
return (int) $matches[1];
|
|
}
|
|
}
|
|
|
|
//TODO: more OS
|
|
|
|
return count(ThreadManager::getInstance()->getAll()) + 2; //MainLogger + Main Thread
|
|
}
|
|
|
|
public static function kill(int $pid, bool $subprocesses) : void{
|
|
$logger = \GlobalLogger::get();
|
|
if($logger instanceof MainLogger){
|
|
$logger->syncFlushBuffer();
|
|
}
|
|
switch(Utils::getOS()){
|
|
case Utils::OS_WINDOWS:
|
|
exec("taskkill.exe /F " . ($subprocesses ? "/T " : "") . "/PID $pid > NUL 2> NUL");
|
|
break;
|
|
case Utils::OS_MACOS:
|
|
case Utils::OS_LINUX:
|
|
default:
|
|
if($subprocesses){
|
|
$pid = -$pid;
|
|
}
|
|
if(function_exists("posix_kill")){
|
|
posix_kill($pid, 9); //SIGKILL
|
|
}else{
|
|
exec("kill -9 $pid > /dev/null 2>&1");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $command Command to execute
|
|
* @param string|null $stdout Reference parameter to write stdout to
|
|
* @param string|null $stderr Reference parameter to write stderr to
|
|
*
|
|
* @return int process exit code
|
|
*/
|
|
public static function execute(string $command, string &$stdout = null, string &$stderr = null) : int{
|
|
$process = proc_open($command, [
|
|
["pipe", "r"],
|
|
["pipe", "w"],
|
|
["pipe", "w"]
|
|
], $pipes);
|
|
|
|
if($process === false){
|
|
$stderr = "Failed to open process";
|
|
$stdout = "";
|
|
|
|
return -1;
|
|
}
|
|
|
|
$stdout = stream_get_contents($pipes[1]);
|
|
$stderr = stream_get_contents($pipes[2]);
|
|
|
|
foreach($pipes as $p){
|
|
fclose($p);
|
|
}
|
|
|
|
return proc_close($process);
|
|
}
|
|
|
|
public static function pid() : int{
|
|
$result = getmypid();
|
|
if($result === false){
|
|
throw new \LogicException("getmypid() doesn't work on this platform");
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public static function uid() : int{
|
|
$result = getmyuid();
|
|
if($result === false){
|
|
throw new \LogicException("getmyuid() doesn't work on this platform");
|
|
}
|
|
return $result;
|
|
}
|
|
}
|