gamemode-tests.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. Copyright (c) 2017-2018, Feral Interactive
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. * Redistributions of source code must retain the above copyright notice,
  7. this list of conditions and the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright
  9. notice, this list of conditions and the following disclaimer in the
  10. documentation and/or other materials provided with the distribution.
  11. * Neither the name of Feral Interactive nor the names of its contributors
  12. may be used to endorse or promote products derived from this software
  13. without specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  18. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  19. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  20. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  21. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  22. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  23. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  24. POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #define _GNU_SOURCE
  27. #include "gamemode.h"
  28. #include "helpers.h"
  29. #include "logging.h"
  30. #include <libgen.h>
  31. #include <sys/types.h>
  32. #include <sys/wait.h>
  33. #include <unistd.h>
  34. #include "daemon_config.h"
  35. #include "gamemode_client.h"
  36. #include "governors-query.h"
  37. #include "gpu-control.h"
  38. /* Initial verify step to ensure gamemode isn't already active */
  39. static int verify_gamemode_initial(void)
  40. {
  41. int status = 0;
  42. if ((status = gamemode_query_status()) != 0 && status != -1) {
  43. LOG_ERROR("gamemode is currently active, tests require gamemode to start deactivated!\n");
  44. status = -1;
  45. } else if (status == -1) {
  46. LOG_ERROR("gamemode_query_status failed: %s!\n", gamemode_error_string());
  47. LOG_ERROR("is gamemode installed correctly?\n");
  48. status = -1;
  49. } else {
  50. status = 0;
  51. }
  52. return status;
  53. }
  54. /* Check if gamemode is active and this client is registered */
  55. static int verify_active_and_registered(void)
  56. {
  57. int status = gamemode_query_status();
  58. if (status != 2) {
  59. if (status == -1) {
  60. LOG_ERROR("gamemode_query_status failed: %s\n", gamemode_error_string());
  61. } else if (status == 1) {
  62. LOG_ERROR("gamemode was active but did not have this process registered\n");
  63. }
  64. LOG_ERROR("gamemode failed to activate correctly when requested (expected 2)!\n");
  65. status = -1;
  66. } else {
  67. status = 0;
  68. }
  69. return status;
  70. }
  71. /* Ensure gamemode is deactivated when it should be */
  72. static int verify_deactivated(void)
  73. {
  74. int status = gamemode_query_status();
  75. if (status != 0) {
  76. if (status == -1) {
  77. LOG_ERROR("gamemode_query_status failed: %s\n", gamemode_error_string());
  78. }
  79. LOG_ERROR("gamemode failed to deactivate when requested (expected 0)!\n");
  80. status = -1;
  81. } else {
  82. status = 0;
  83. }
  84. return status;
  85. }
  86. /* Ensure another client is connected */
  87. static int verify_other_client_connected(void)
  88. {
  89. int status = gamemode_query_status();
  90. if (status != 1) {
  91. if (status == -1) {
  92. LOG_ERROR("gamemode_query_status failed: %s\n", gamemode_error_string());
  93. }
  94. LOG_ERROR("gamemode_query_status failed to return other client connected (expected 1)!\n");
  95. status = -1;
  96. } else {
  97. status = 0;
  98. }
  99. return status;
  100. }
  101. /* Run basic client tests
  102. * Tests a simple request_start and request_end works
  103. */
  104. static int run_basic_client_tests(void)
  105. {
  106. LOG_MSG(":: Basic client tests\n");
  107. /* First verify that gamemode is not currently active on the system
  108. * As well as it being currently installed and queryable
  109. */
  110. if (verify_gamemode_initial() != 0)
  111. return -1;
  112. /* Verify that gamemode_request_start correctly start gamemode */
  113. if (gamemode_request_start() != 0) {
  114. LOG_ERROR("gamemode_request_start failed: %s\n", gamemode_error_string());
  115. return -1;
  116. }
  117. /* Verify that gamemode is now active and this client is registered*/
  118. if (verify_active_and_registered() != 0)
  119. return -1;
  120. /* Verify that gamemode_request_end corrently de-registers gamemode */
  121. if (gamemode_request_end() != 0) {
  122. LOG_ERROR("gamemode_request_end failed: %s!\n", gamemode_error_string());
  123. return -1;
  124. }
  125. /* Verify that gamemode is now innactive */
  126. if (verify_deactivated() != 0)
  127. return -1;
  128. LOG_MSG(":: Passed\n\n");
  129. return 0;
  130. }
  131. /* Run some dual client tests
  132. * This also tests that the "-r" argument works correctly and cleans up correctly
  133. */
  134. static int run_dual_client_tests(void)
  135. {
  136. int status = 0;
  137. /* Try running some process interop tests */
  138. LOG_MSG(":: Dual client tests\n");
  139. /* Get the current path to this binary */
  140. char mypath[PATH_MAX];
  141. memset(mypath, 0, sizeof(mypath));
  142. if (readlink("/proc/self/exe", mypath, PATH_MAX) == -1) {
  143. LOG_ERROR("could not read current exe path: %s\n", strerror(errno));
  144. return -1;
  145. }
  146. /* Fork so that the child can request gamemode */
  147. int child = fork();
  148. if (child == 0) {
  149. /* Relaunch self with -r (request and wait for signal) */
  150. if (execl(mypath, mypath, "-r", (char *)NULL) == -1) {
  151. LOG_ERROR("failed to re-launch self (%s) with execl: %s\n", mypath, strerror(errno));
  152. return -1;
  153. }
  154. }
  155. /* Parent process */
  156. /* None of these should early-out as we need to clean up the child */
  157. /* Give the child a chance to reqeust gamemode */
  158. usleep(1000);
  159. /* Check that when we request gamemode, it replies that the other client is connected */
  160. if (verify_other_client_connected() != 0)
  161. status = -1;
  162. /* Verify that gamemode_request_start correctly start gamemode */
  163. if (gamemode_request_start() != 0) {
  164. LOG_ERROR("gamemode_request_start failed: %s\n", gamemode_error_string());
  165. status = -1;
  166. }
  167. /* Verify that gamemode is now active and this client is registered*/
  168. if (verify_active_and_registered() != 0)
  169. status = -1;
  170. /* Request end of gamemode (de-register ourselves) */
  171. if (gamemode_request_end() != 0) {
  172. LOG_ERROR("gamemode_request_end failed: %s!\n", gamemode_error_string());
  173. status = -1;
  174. }
  175. /* Check that when we request gamemode, it replies that the other client is connected */
  176. if (verify_other_client_connected() != 0)
  177. status = -1;
  178. /* Send SIGINT to child to wake it up*/
  179. if (kill(child, SIGINT) == -1) {
  180. LOG_ERROR("failed to send continue signal to other client: %s\n", strerror(errno));
  181. status = -1;
  182. }
  183. /* Give the child a chance to finish */
  184. usleep(10000);
  185. // Wait for the child to finish up
  186. int wstatus;
  187. while (waitpid(child, &wstatus, WNOHANG) == 0) {
  188. LOG_MSG("...Waiting for child to quit...\n");
  189. usleep(10000);
  190. }
  191. /* Verify that gamemode is now innactive */
  192. if (verify_deactivated() != 0)
  193. return -1;
  194. if (status == 0)
  195. LOG_MSG(":: Passed\n\n");
  196. return status;
  197. }
  198. static int run_cpu_governor_tests(struct GameModeConfig *config)
  199. {
  200. /* get the two config parameters we care about */
  201. char desiredgov[CONFIG_VALUE_MAX] = { 0 };
  202. config_get_desired_governor(config, desiredgov);
  203. if (desiredgov[0] == '\0')
  204. strcpy(desiredgov, "performance");
  205. char defaultgov[CONFIG_VALUE_MAX] = { 0 };
  206. config_get_default_governor(config, defaultgov);
  207. if (desiredgov[0] == '\0') {
  208. const char *currentgov = get_gov_state();
  209. if (currentgov) {
  210. strncpy(desiredgov, currentgov, CONFIG_VALUE_MAX);
  211. } else {
  212. LOG_ERROR(
  213. "Could not get current CPU governor state, this indicates an error! See rest "
  214. "of log.\n");
  215. return -1;
  216. }
  217. }
  218. /* Start gamemode */
  219. gamemode_request_start();
  220. /* Verify the governor is the desired one */
  221. const char *currentgov = get_gov_state();
  222. if (strncmp(currentgov, desiredgov, CONFIG_VALUE_MAX) != 0) {
  223. LOG_ERROR("Govenor was not set to %s (was actually %s)!", desiredgov, currentgov);
  224. gamemode_request_end();
  225. return -1;
  226. }
  227. /* End gamemode */
  228. gamemode_request_end();
  229. /* Verify the governor has been set back */
  230. currentgov = get_gov_state();
  231. if (strncmp(currentgov, defaultgov, CONFIG_VALUE_MAX) != 0) {
  232. LOG_ERROR("Govenor was not set back to %s (was actually %s)!", defaultgov, currentgov);
  233. return -1;
  234. }
  235. return 0;
  236. }
  237. static int run_custom_scripts_tests(struct GameModeConfig *config)
  238. {
  239. int scriptstatus = 0;
  240. /* Grab and test the start scripts */
  241. char startscripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  242. memset(startscripts, 0, sizeof(startscripts));
  243. config_get_gamemode_start_scripts(config, startscripts);
  244. if (startscripts[0][0] != '\0') {
  245. int i = 0;
  246. while (*startscripts[i] != '\0' && i < CONFIG_LIST_MAX) {
  247. LOG_MSG(":::: Running start script [%s]\n", startscripts[i]);
  248. int ret = system(startscripts[i]);
  249. if (ret == 0)
  250. LOG_MSG(":::: Passed\n");
  251. else {
  252. LOG_MSG(":::: Failed!\n");
  253. scriptstatus = -1;
  254. }
  255. i++;
  256. }
  257. }
  258. /* Grab and test the end scripts */
  259. char endscripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  260. memset(endscripts, 0, sizeof(endscripts));
  261. config_get_gamemode_end_scripts(config, endscripts);
  262. if (endscripts[0][0] != '\0') {
  263. int i = 0;
  264. while (*endscripts[i] != '\0' && i < CONFIG_LIST_MAX) {
  265. LOG_MSG(":::: Running end script [%s]\n", endscripts[i]);
  266. int ret = system(endscripts[i]);
  267. if (ret == 0)
  268. LOG_MSG(":::: Passed\n");
  269. else {
  270. LOG_MSG(":::: Failed!\n");
  271. scriptstatus = -1;
  272. }
  273. i++;
  274. }
  275. }
  276. /* Specal value for no scripts */
  277. if (endscripts[0][0] == '\0' && startscripts[0][0] == '\0')
  278. return 1;
  279. return scriptstatus;
  280. }
  281. int run_gpu_optimisation_tests(struct GameModeConfig *config)
  282. {
  283. /* First check if these are turned on */
  284. char apply[CONFIG_VALUE_MAX];
  285. config_get_apply_gpu_optimisations(config, apply);
  286. if (strlen(apply) == 0) {
  287. /* Special value for disabled */
  288. return 1;
  289. } else if (strncmp(apply, "accept-responsibility", CONFIG_VALUE_MAX) != 0) {
  290. LOG_ERROR(
  291. "apply_gpu_optimisations set to value other than \"accept-responsibility\" (%s), will "
  292. "not apply GPU optimisations!\n",
  293. apply);
  294. return -1;
  295. }
  296. /* Get current GPU values */
  297. GameModeGPUInfo gpuinfo;
  298. gpuinfo.device = config_get_gpu_device(config);
  299. gpuinfo.vendor = config_get_gpu_vendor(config);
  300. if (gpuinfo.vendor == Vendor_NVIDIA)
  301. gpuinfo.nv_perf_level = config_get_nv_perf_level(config);
  302. if (game_mode_get_gpu(&gpuinfo) != 0) {
  303. LOG_ERROR("Could not get current GPU info, see above!\n");
  304. return -1;
  305. }
  306. /* TODO continue to run gamemode and check GPU stats */
  307. return 0;
  308. }
  309. /**
  310. * game_mode_run_feature_tests runs a set of tests for each current feature (based on the current
  311. * config) returns 0 for success, -1 for failure
  312. */
  313. static int game_mode_run_feature_tests(void)
  314. {
  315. int status = 0;
  316. LOG_MSG(":: Feature tests\n");
  317. /* If we reach here, we should assume the basic requests and register functions are working */
  318. /* Grab the config */
  319. /* Note: this config may pick up a local gamemode.ini, or the daemon may have one, we may need
  320. * to cope with that */
  321. GameModeConfig *config = config_create();
  322. config_init(config);
  323. /* Does the CPU governor get set properly? */
  324. {
  325. LOG_MSG("::: Verifying CPU governor setting\n");
  326. int cpustatus = run_cpu_governor_tests(config);
  327. if (cpustatus == 0)
  328. LOG_MSG("::: Passed\n");
  329. else {
  330. LOG_MSG("::: Failed!\n");
  331. // Consider the CPU governor feature required
  332. status = 1;
  333. }
  334. }
  335. /* Do custom scripts run? */
  336. {
  337. LOG_MSG("::: Verifying Scripts\n");
  338. int scriptstatus = run_custom_scripts_tests(config);
  339. if (scriptstatus == 1)
  340. LOG_MSG("::: Passed (no scripts configured to run)\n");
  341. else if (scriptstatus == 0)
  342. LOG_MSG("::: Passed\n");
  343. else {
  344. LOG_MSG("::: Failed!\n");
  345. // Any custom scripts should be expected to work
  346. status = 1;
  347. }
  348. }
  349. /* Do GPU optimisations get applied? */
  350. {
  351. int gpustatus = run_gpu_optimisation_tests(config);
  352. if (gpustatus == 1)
  353. LOG_MSG("::: Passed (gpu optimisations not configured to run)\n");
  354. else if (gpustatus == 0)
  355. LOG_MSG("::: Passed\n");
  356. else {
  357. LOG_MSG("::: Failed!\n");
  358. // Any custom scripts should be expected to work
  359. status = 1;
  360. }
  361. }
  362. /* Does the screensaver get inhibited? */
  363. /* TODO: Unknown if this is testable, org.freedesktop.ScreenSaver has no query method */
  364. /* Was the process reniced? */
  365. /* Was the scheduling applied? */
  366. /* Were io priorities changed? */
  367. /* Note: These don't get cleared up on un-register, so will have already been applied */
  368. /* TODO */
  369. if (status != -1)
  370. LOG_MSG(":: Passed%s\n\n", status > 0 ? " (with optional failures)" : "");
  371. else
  372. LOG_ERROR(":: Failed!\n");
  373. return status;
  374. }
  375. /**
  376. * game_mode_run_client_tests runs a set of tests of the client code
  377. * we simply verify that the client can request the status and recieves the correct results
  378. *
  379. * returns 0 for success, -1 for failure
  380. */
  381. int game_mode_run_client_tests()
  382. {
  383. int status = 0;
  384. LOG_MSG(": Running tests\n\n");
  385. /* Run the basic tests */
  386. if (run_basic_client_tests() != 0)
  387. status = -1;
  388. /* Run the dual client tests */
  389. if (run_dual_client_tests() != 0)
  390. status = -1;
  391. if (status != 0) {
  392. LOG_MSG(": Client tests failed, skipping feature tests\n");
  393. } else {
  394. /* Run the feature tests */
  395. status = game_mode_run_feature_tests();
  396. }
  397. if (status >= 0)
  398. LOG_MSG(": All Tests Passed%s!\n", status > 0 ? " (with optional failures)" : "");
  399. else
  400. LOG_MSG(": Tests Failed!\n");
  401. return status;
  402. }