[igt-dev] [PATCH i-g-t] Use the new procps library libproc2

Petri Latvala petri.latvala at intel.com
Thu Nov 17 11:29:30 UTC 2022


On Thu, Nov 17, 2022 at 12:17:38PM +0100, Kamil Konieczny wrote:
> Hi Craig,
> 
> On 2022-11-17 at 10:41:29 +0200, Petri Latvala wrote:
> > From: Craig Small <csmall at dropbear.xyz>
> > 
> > ---
> >  lib/igt_aux.c        |  246 +++++-
> >  lib/igt_aux.c.orig   | 1921 ++++++++++++++++++++++++++++++++++++++++++
> -- ^^^^^^^^^^^^^^^^^^
> 
> >  lib/meson.build      |    7 +-
> >  lib/meson.build.orig |  358 ++++++++
> -- ^^^^^^^^^^^^^^^^^^^^
> 
> These should not be there ?

Those are my mistakes.


-- 
Petri Latvala


> 
> >  meson.build          |   10 +-
> >  5 files changed, 2500 insertions(+), 42 deletions(-)
> >  create mode 100644 lib/igt_aux.c.orig
> >  create mode 100644 lib/meson.build.orig
> > 
> > diff --git a/lib/igt_aux.c b/lib/igt_aux.c
> > index 15e30440..2aee1275 100644
> > --- a/lib/igt_aux.c
> > +++ b/lib/igt_aux.c
> > @@ -52,8 +52,16 @@
> >  #include <assert.h>
> >  #include <grp.h>
> >  
> > +#ifdef HAVE_LIBPROCPS
> >  #include <proc/readproc.h>
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> -- ^
> Maybe just #elif ?
> 
> > +#include <libproc2/pids.h>
> > +#endif
> > +
> >  #include <libudev.h>
> > +#include <linux/limits.h>
> -- ^^^^^^^^^^^^^^^^^^
> > +#include <dirent.h>
> -- ^^^^^^^^^^^^^^^^^^
> What this has with libproc2 ?
> Note also that FreeBSD have its own limits.h
> 
> >  
> >  #include "drmtest.h"
> >  #include "i915_drm.h"
> > @@ -1217,6 +1225,7 @@ void igt_unlock_mem(void)
> >   */
> >  int igt_is_process_running(const char *comm)
> >  {
> > +#if HAVE_LIBPROCPS
> >  	PROCTAB *proc;
> >  	proc_t *proc_info;
> >  	bool found = false;
> > @@ -1235,6 +1244,26 @@ int igt_is_process_running(const char *comm)
> >  
> >  	closeproc(proc);
> >  	return found;
> > +#endif
> > +#if HAVE_LIBPROC2
> > +	enum pids_item Item[] = { PIDS_CMD };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +	char *pid_comm;
> > +	bool found = false;
> > +
> > +	if (procps_pids_new(&info, Item, 1) < 0)
> > +	    return false;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +	    pid_comm = PIDS_VAL(0, str, stack, info);
> > +	    if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
> 
> Processes AA and aa are different, why do you use strncasecmp ?
> 
> > +		found = true;
> > +		break;
> > +	    }
> > +	}
> > +	procps_pids_unref(&info);
> > +	return found;
> > +#endif
> >  }
> >  
> >  /**
> > @@ -1251,6 +1280,7 @@ int igt_is_process_running(const char *comm)
> >   */
> >  int igt_terminate_process(int sig, const char *comm)
> >  {
> > +#if HAVE_LIBPROCPS
> >  	PROCTAB *proc;
> >  	proc_t *proc_info;
> >  	int err = 0;
> > @@ -1272,6 +1302,29 @@ int igt_terminate_process(int sig, const char *comm)
> >  
> >  	closeproc(proc);
> >  	return err;
> > +#endif
> > +#if HAVE_LIBPROC2
> > +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +	char *pid_comm;
> > +	int pid;
> > +	int err = 0;
> > +
> > +	if (procps_pids_new(&info, Items, 2) < 0)
> > +		return -errno;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +		pid = PIDS_VAL(0, s_int, stack, info);
> > +		pid_comm = PIDS_VAL(1, str, stack, info);
> > +		if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
> > +			if (kill(pid, sig) < 0)
> > +				err = -errno;
> > +			break;
> > +		}
> > +	}
> > +	procps_pids_unref(&info);
> > +	return err;
> > +#endif
> >  }
> >  
> >  struct pinfo {
> > @@ -1341,9 +1394,9 @@ igt_show_stat_header(void)
> >  }
> >  
> >  static void
> > -igt_show_stat(proc_t *info, int *state, const char *fn)
> > +igt_show_stat(const pid_t tid, const char *cmd, int *state, const char *fn)
> >  {
> > -	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> > +	struct pinfo p = { .pid = tid, .comm = cmd, .fn = fn };
> >  
> >  	if (!*state)
> >  		igt_show_stat_header();
> > @@ -1353,7 +1406,7 @@ igt_show_stat(proc_t *info, int *state, const char *fn)
> >  }
> >  
> >  static void
> > -__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
> > +__igt_lsof_fds(const pid_t tid, const char *cmd, int *state, char *proc_path, const char *dir)
> >  {
> >  	struct dirent *d;
> >  	struct stat st;
> > @@ -1400,7 +1453,7 @@ again:
> >  		dirn = dirname(copy_fd_lnk);
> >  
> >  		if (!strncmp(dir, dirn, strlen(dir)))
> > -			igt_show_stat(proc_info, state, fd_lnk);
> > +			igt_show_stat(tid, cmd, state, fd_lnk);
> >  
> >  		free(copy_fd_lnk);
> >  		free(fd_lnk);
> > @@ -1416,13 +1469,14 @@ again:
> >  static void
> >  __igt_lsof(const char *dir)
> >  {
> > -	PROCTAB *proc;
> > -	proc_t *proc_info;
> > -
> >  	char path[30];
> >  	char *name_lnk;
> >  	struct stat st;
> >  	int state = 0;
> > +#ifdef HAVE_LIBPROCPS
> > +	PROCTAB *proc;
> > +	proc_t *proc_info;
> > +
> >  
> >  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> >  	igt_assert(proc != NULL);
> > @@ -1443,19 +1497,57 @@ __igt_lsof(const char *dir)
> >  		name_lnk[read] = '\0';
> >  
> >  		if (!strncmp(dir, name_lnk, strlen(dir)))
> > -			igt_show_stat(proc_info, &state, name_lnk);
> > +			igt_show_stat(proc_info->tid, proc_info->cmd, &state, name_lnk);
> >  
> >  		/* check also fd, seems that lsof(8) doesn't look here */
> >  		memset(path, 0, sizeof(path));
> >  		snprintf(path, sizeof(path), "/proc/%d/fd", proc_info->tid);
> >  
> > -		__igt_lsof_fds(proc_info, &state, path, dir);
> > +		__igt_lsof_fds(proc_info->tid, proc_info->cmd, &state, path, dir);
> >  
> >  		free(name_lnk);
> >  		freeproc(proc_info);
> >  	}
> >  
> >  	closeproc(proc);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +
> > +	if (procps_pids_new(&info, Items, 2) < 0)
> > +		return;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +		ssize_t read;
> > +		int tid = PIDS_VAL(0, s_int, stack, info);
> > +		char *pid_comm = PIDS_VAL(1, str, stack, info);
> > +
> > +		/* check current working directory */
> > +		memset(path, 0, sizeof(path));
> > +		snprintf(path, sizeof(path), "/proc/%d/cwd", tid);
> > +
> > +		if (stat(path, &st) == -1)
> > +			continue;
> > +
> > +		name_lnk = malloc(st.st_size + 1);
> > +
> > +		igt_assert((read = readlink(path, name_lnk, st.st_size + 1)));
> > +		name_lnk[read] = '\0';
> > +
> > +		if (!strncmp(dir, name_lnk, strlen(dir)))
> > +			igt_show_stat(tid, pid_comm, &state, name_lnk);
> > +
> > +		/* check also fd, seems that lsof(8) doesn't look here */
> > +		memset(path, 0, sizeof(path));
> > +		snprintf(path, sizeof(path), "/proc/%d/fd", tid);
> > +
> > +		__igt_lsof_fds(tid, pid_comm, &state, path, dir);
> > +
> > +		free(name_lnk);
> > +	}
> > +	procps_pids_unref(&info);
> > +#endif
> >  }
> >  
> >  /**
> > @@ -1490,7 +1582,7 @@ igt_lsof(const char *dpath)
> >  	free(sanitized);
> >  }
> >  
> > -static void pulseaudio_unload_module(proc_t *proc_info)
> > +static void pulseaudio_unload_module(const uid_t euid, const gid_t egid)
> >  {
> >  	struct igt_helper_process pa_proc = {};
> >  	char xdg_dir[PATH_MAX];
> > @@ -1498,14 +1590,14 @@ static void pulseaudio_unload_module(proc_t *proc_info)
> >  	struct passwd *pw;
> >  
> >  	igt_fork_helper(&pa_proc) {
> > -		pw = getpwuid(proc_info->euid);
> > +		pw = getpwuid(euid);
> >  		homedir = pw->pw_dir;
> > -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
> >  
> >  		igt_info("Request pulseaudio to stop using audio device\n");
> >  
> > -		setgid(proc_info->egid);
> > -		setuid(proc_info->euid);
> > +		setgid(egid);
> > +		setuid(euid);
> >  		clearenv();
> >  		setenv("HOME", homedir, 1);
> >  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> > @@ -1524,10 +1616,13 @@ static void pipewire_reserve_wait(void)
> >  	char xdg_dir[PATH_MAX];
> >  	const char *homedir;
> >  	struct passwd *pw;
> > -	proc_t *proc_info;
> > -	PROCTAB *proc;
> > +	int tid=0, euid, egid;
> >  
> > +#ifdef HAVE_LIBPROCPS
> >  	igt_fork_helper(&pw_reserve_proc) {
> > +		proc_t *proc_info;
> > +		PROCTAB *proc;
> > +
> >  		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> >  
> >  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > @@ -1540,19 +1635,44 @@ static void pipewire_reserve_wait(void)
> >  		}
> >  		closeproc(proc);
> >  
> > +		tid = proc_info->tid;
> > +		euid = proc_info->euid;
> > +		egid = proc_info->egid;
> > +		freeproc(proc_info);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +	igt_fork(child, 1) {
> 
> This should be fork_helper like above.
> 
> > +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID, PIDS_ID_EGID };
> > +		enum rel_items { EU_PID, EU_EUID, EU_EGID };
> > +		struct pids_info *info = NULL;
> > +		struct pids_stack *stack;
> > +
> > +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> > +
> > +		if (procps_pids_new(&info, Items, 3) < 0)
> > +		    return;
> ------------------- ^
> Use exit(0) here plus maybe some ing_info why it failed ?
> 
> > +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +			tid = PIDS_VAL(EU_PID, s_int, stack, info);
> > +			if (pipewire_pulse_pid == tid)
> > +				break;
> > +		}
> > +		euid = PIDS_VAL(EU_EUID, s_int, stack, info);
> > +		egid = PIDS_VAL(EU_EGID, s_int, stack, info);
> > +		procps_pids_unref(&info);
> > +#endif
> > +
> >  		/* Sanity check: if it can't find the process, it means it has gone */
> > -		if (pipewire_pulse_pid != proc_info->tid)
> > +		if (pipewire_pulse_pid != tid)
> >  			exit(0);
> >  
> > -		pw = getpwuid(proc_info->euid);
> > +		pw = getpwuid(euid);
> >  		homedir = pw->pw_dir;
> > -		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > -		setgid(proc_info->egid);
> > -		setuid(proc_info->euid);
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
> > +		setgid(egid);
> > +		setuid(euid);
> >  		clearenv();
> >  		setenv("HOME", homedir, 1);
> >  		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> > -		freeproc(proc_info);
> >  
> >  		/*
> >  		 * pw-reserve will run in background. It will only exit when
> > @@ -1570,9 +1690,7 @@ static void pipewire_reserve_wait(void)
> >  int pipewire_pulse_start_reserve(void)
> >  {
> >  	bool is_pw_reserve_running = false;
> > -	proc_t *proc_info;
> >  	int attempts = 0;
> > -	PROCTAB *proc;
> >  
> >  	if (!pipewire_pulse_pid)
> >  		return 0;
> > @@ -1584,6 +1702,10 @@ int pipewire_pulse_start_reserve(void)
> >  	 * pipewire version 0.3.50 or upper.
> >  	 */
> >  	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> > +#ifdef HAVE_LIBPROCPS
> > +		PROCTAB *proc;
> > +		proc_t *proc_info;
> > +
> >  		usleep(1000);
> >  		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> >  		igt_assert(proc != NULL);
> > @@ -1598,6 +1720,25 @@ int pipewire_pulse_start_reserve(void)
> >  			freeproc(proc_info);
> >  		}
> >  		closeproc(proc);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +		enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
> > +		struct pids_info *info = NULL;
> > +		struct pids_stack *stack;
> > +
> > +		usleep(1000);
> > +
> > +		if (procps_pids_new(&info, Items, 2) < 0)
> > +			return 1;
> ----------------------- ^
> break or igt_assert_f(errno == 0, "Getting procps failed\n"); here.
> 
> > +		while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +			if (!strcmp(PIDS_VAL(1, str, stack, info), "pw-reserve")) {
> > +				is_pw_reserve_running = true;
> > +				pipewire_pw_reserve_pid = PIDS_VAL(0, s_int, stack, info);
> > +				break;
> > +			}
> > +		}
> > +		procps_pids_unref(&info);
> > +#endif
> >  		if (is_pw_reserve_running)
> >  			break;
> >  	}
> > @@ -1645,7 +1786,7 @@ void pipewire_pulse_stop_reserve(void)
> >   * If the check fails, it means that the process can simply be killed.
> >   */
> >  static int
> > -__igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> > +__igt_lsof_audio_and_kill_proc(const pid_t tid, const char *cmd, const uid_t euid, const gid_t egid, char *proc_path)
> >  {
> >  	const char *audio_dev = "/dev/snd/";
> >  	char path[PATH_MAX * 2];
> > @@ -1670,10 +1811,10 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> >  	 * 2) unload/unbind the the audio driver(s);
> >  	 * 3) stop the pw-reserve thread.
> >  	 */
> > -	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> > +	if (!strcmp(cmd, "pipewire-pulse")) {
> >  		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> > -			 proc_info->tid, proc_info->cmd);
> > -		pipewire_pulse_pid = proc_info->tid;
> > +			 tid, cmd);
> > +		pipewire_pulse_pid = tid;
> >  		return 0;
> >  	}
> >  	/*
> > @@ -1685,9 +1826,9 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> >  	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
> >  	 * when the time comes.
> >  	 */
> > -	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> > +	if (!strcmp(cmd, "pipewire-media-session"))
> >  		return 0;
> > -	if (!strcmp(proc_info->cmd, "wireplumber"))
> > +	if (!strcmp(cmd, "wireplumber"))
> >  		return 0;
> >  
> >  	dp = opendir(proc_path);
> > @@ -1723,22 +1864,22 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> >  		 * enough to unbind audio modules and won't cause race issues
> >  		 * with systemd trying to reload it.
> >  		 */
> > -		if (!strcmp(proc_info->cmd, "pulseaudio")) {
> > -			pulseaudio_unload_module(proc_info);
> > +		if (!strcmp(cmd, "pulseaudio")) {
> > +			pulseaudio_unload_module(euid, egid);
> >  			break;
> >  		}
> >  
> >  		/* For all other processes, just kill them */
> >  		igt_info("process %d (%s) is using audio device. Should be terminated.\n",
> > -				proc_info->tid, proc_info->cmd);
> > +				tid, cmd);
> >  
> > -		if (kill(proc_info->tid, SIGTERM) < 0) {
> > +		if (kill(tid, SIGTERM) < 0) {
> >  			igt_info("Fail to terminate %s (pid: %d) with SIGTERM\n",
> > -				proc_info->cmd, proc_info->tid);
> > -			if (kill(proc_info->tid, SIGABRT) < 0) {
> > +				cmd, tid);
> > +			if (kill(tid, SIGABRT) < 0) {
> >  				fail++;
> >  				igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
> > -					proc_info->cmd, proc_info->tid);
> > +					cmd, tid);
> >  			}
> >  		}
> >  
> > @@ -1760,23 +1901,48 @@ int
> >  igt_lsof_kill_audio_processes(void)
> >  {
> >  	char path[PATH_MAX];
> > +	int fail = 0;
> > +
> > +#ifdef HAVE_LIBPROCPS
> >  	proc_t *proc_info;
> >  	PROCTAB *proc;
> > -	int fail = 0;
> >  
> >  	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> >  	igt_assert(proc != NULL);
> >  	pipewire_pulse_pid = 0;
> > -
> >  	while ((proc_info = readproc(proc, NULL))) {
> >  		if (snprintf(path, sizeof(path), "/proc/%d/fd", proc_info->tid) < 1)
> >  			fail++;
> >  		else
> > -			fail += __igt_lsof_audio_and_kill_proc(proc_info, path);
> > +			fail += __igt_lsof_audio_and_kill_proc(proc_info->tid, proc_info->cmd, proc_info->euid, proc_info->egid, path);
> >  
> >  		freeproc(proc_info);
> >  	}
> >  	closeproc(proc);
> > +#endif
> > +#ifdef HAVE_LIBPROC2
> > +	enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_ID_EUID, PIDS_ID_EGID };
> > +	enum rel_items { EU_PID, EU_CMD, EU_EUID, EU_EGID };
> > +	struct pids_info *info = NULL;
> > +	struct pids_stack *stack;
> > +	pid_t tid;
> > +
> > +	if (procps_pids_new(&info, Items, 4) < 0)
> > +		return 1;
> > +	while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
> > +		tid = PIDS_VAL(EU_PID, s_int, stack, info);
> > +
> > +		if (snprintf(path, sizeof(path), "/proc/%d/fd", tid) < 1)
> > +			fail++;
> > +		else
> > +			fail += __igt_lsof_audio_and_kill_proc(tid,
> > +				PIDS_VAL(EU_CMD, str, stack, info),
> > +				PIDS_VAL(EU_EUID, s_int, stack, info),
> > +				PIDS_VAL(EU_EGID, s_int, stack, info),
> > +				path);
> > +	}
> > +	procps_pids_unref(&info);
> > +#endif
> >  
> >  	return fail;
> >  }
> > diff --git a/lib/igt_aux.c.orig b/lib/igt_aux.c.orig
> > new file mode 100644
> > index 00000000..15e30440
> > --- /dev/null
> > +++ b/lib/igt_aux.c.orig
> > @@ -0,0 +1,1921 @@
> > +/*
> > + * Copyright © 2007, 2011, 2013, 2014, 2015 Intel Corporation
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > + * IN THE SOFTWARE.
> > + *
> > + * Authors:
> > + *    Eric Anholt <eric at anholt.net>
> > + *    Daniel Vetter <daniel.vetter at ffwll.ch>
> > + *
> > + */
> > +
> > +#ifdef HAVE_LIBGEN_H
> > +#include <libgen.h>
> > +#endif
> > +#include <stdio.h>
> > +#include <fcntl.h>
> > +#include <pwd.h>
> > +#include <sys/stat.h>
> > +#include <sys/ioctl.h>
> > +#include <string.h>
> > +#include <sys/mman.h>
> > +#include <signal.h>
> > +#include <pciaccess.h>
> > +#include <stdlib.h>
> > +#include <time.h>
> > +#include <unistd.h>
> > +#include <sys/poll.h>
> > +#include <sys/wait.h>
> > +#include <sys/resource.h>
> > +#include <sys/time.h>
> > +#include <sys/types.h>
> > +#include <sys/syscall.h>
> > +#include <sys/utsname.h>
> > +#include <termios.h>
> > +#include <assert.h>
> > +#include <grp.h>
> > +
> > +#include <proc/readproc.h>
> > +#include <libudev.h>
> > +
> > +#include "drmtest.h"
> > +#include "i915_drm.h"
> > +#include "intel_chipset.h"
> > +#include "igt_aux.h"
> > +#include "igt_debugfs.h"
> > +#include "igt_gt.h"
> > +#include "igt_params.h"
> > +#include "igt_rand.h"
> > +#include "igt_sysfs.h"
> > +#include "config.h"
> > +#include "intel_reg.h"
> > +#include "ioctl_wrappers.h"
> > +#include "igt_kms.h"
> > +#include "igt_stats.h"
> > +#include "igt_sysfs.h"
> > +
> > +#ifdef HAVE_LIBGEN_H
> > +#include <libgen.h>   /* for dirname() */
> > +#endif
> > +
> > +/**
> > + * SECTION:igt_aux
> > + * @short_description: Auxiliary libraries and support functions
> > + * @title: aux
> > + * @include: igt.h
> > + *
> > + * This library provides various auxiliary helper functions that don't really
> > + * fit into any other topic.
> > + */
> > +
> > +static struct __igt_sigiter_global {
> > +	pid_t tid;
> > +	timer_t timer;
> > +	struct timespec offset;
> > +	struct {
> > +		long hit, miss;
> > +		long ioctls, signals;
> > +	} stat;
> > +} __igt_sigiter;
> > +
> > +static void sigiter(int sig, siginfo_t *info, void *arg)
> > +{
> > +	__igt_sigiter.stat.signals++;
> > +}
> > +
> > +#if 0
> > +#define SIG_ASSERT(expr) igt_assert(expr)
> > +#else
> > +#define SIG_ASSERT(expr)
> > +#endif
> > +
> > +static int
> > +sig_ioctl(int fd, unsigned long request, void *arg)
> > +{
> > +	struct itimerspec its;
> > +	int ret;
> > +
> > +	SIG_ASSERT(__igt_sigiter.timer);
> > +	SIG_ASSERT(__igt_sigiter.tid == gettid());
> > +
> > +	memset(&its, 0, sizeof(its));
> > +	if (timer_settime(__igt_sigiter.timer, 0, &its, NULL)) {
> > +		/* oops, we didn't undo the interrupter (i.e. !unwound abort) */
> > +		igt_ioctl = drmIoctl;
> > +		return drmIoctl(fd, request, arg);
> > +	}
> > +
> > +	its.it_value = __igt_sigiter.offset;
> > +	do {
> > +		long serial;
> > +
> > +		__igt_sigiter.stat.ioctls++;
> > +
> > +		ret = 0;
> > +		serial = __igt_sigiter.stat.signals;
> > +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> > +		if (ioctl(fd, request, arg))
> > +			ret = errno;
> > +		if (__igt_sigiter.stat.signals == serial)
> > +			__igt_sigiter.stat.miss++;
> > +		if (ret == 0)
> > +			break;
> > +
> > +		if (ret == EINTR) {
> > +			__igt_sigiter.stat.hit++;
> > +
> > +			its.it_value.tv_sec *= 2;
> > +			its.it_value.tv_nsec *= 2;
> > +			while (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> > +				its.it_value.tv_nsec -= NSEC_PER_SEC;
> > +				its.it_value.tv_sec += 1;
> > +			}
> > +
> > +			SIG_ASSERT(its.it_value.tv_nsec >= 0);
> > +			SIG_ASSERT(its.it_value.tv_sec >= 0);
> > +		}
> > +	} while (ret == EAGAIN || ret == EINTR);
> > +
> > +	memset(&its, 0, sizeof(its));
> > +	timer_settime(__igt_sigiter.timer, 0, &its, NULL);
> > +
> > +	errno = ret;
> > +	return ret ? -1 : 0;
> > +}
> > +
> > +static bool igt_sigiter_start(struct __igt_sigiter *iter, bool enable)
> > +{
> > +	/* Note that until we can automatically clean up on failed/skipped
> > +	 * tests, we cannot assume the state of the igt_ioctl indirection.
> > +	 */
> > +	SIG_ASSERT(igt_ioctl == drmIoctl);
> > +	igt_ioctl = drmIoctl;
> > +
> > +	if (enable) {
> > +		struct timespec start, end;
> > +		struct sigevent sev;
> > +		struct sigaction act;
> > +		struct itimerspec its;
> > +
> > +		igt_ioctl = sig_ioctl;
> > +		__igt_sigiter.tid = gettid();
> > +
> > +		memset(&sev, 0, sizeof(sev));
> > +		sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> > +		sev.sigev_notify_thread_id = __igt_sigiter.tid;
> > +		sev.sigev_signo = SIGRTMIN;
> > +		igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &__igt_sigiter.timer) == 0);
> > +
> > +		memset(&its, 0, sizeof(its));
> > +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> > +
> > +		memset(&act, 0, sizeof(act));
> > +		act.sa_sigaction = sigiter;
> > +		act.sa_flags = SA_SIGINFO;
> > +		igt_assert(sigaction(SIGRTMIN, &act, NULL) == 0);
> > +
> > +		/* Try to find the approximate delay required to skip over
> > +		 * the timer_setttime and into the following ioctl() to try
> > +		 * and avoid the timer firing before we enter the drmIoctl.
> > +		 */
> > +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0);
> > +		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
> > +		igt_assert(clock_gettime(CLOCK_MONOTONIC, &end) == 0);
> > +
> > +		__igt_sigiter.offset.tv_sec = end.tv_sec - start.tv_sec;
> > +		__igt_sigiter.offset.tv_nsec = end.tv_nsec - start.tv_nsec;
> > +		if (__igt_sigiter.offset.tv_nsec < 0) {
> > +			__igt_sigiter.offset.tv_nsec += NSEC_PER_SEC;
> > +			__igt_sigiter.offset.tv_sec -= 1;
> > +		}
> > +		if (__igt_sigiter.offset.tv_sec < 0) {
> > +			__igt_sigiter.offset.tv_nsec = 0;
> > +			__igt_sigiter.offset.tv_sec = 0;
> > +		}
> > +		igt_assert(__igt_sigiter.offset.tv_sec == 0);
> > +
> > +		igt_debug("Initial delay for interruption: %ld.%09lds\n",
> > +			  __igt_sigiter.offset.tv_sec,
> > +			  __igt_sigiter.offset.tv_nsec);
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static bool igt_sigiter_stop(struct __igt_sigiter *iter, bool enable)
> > +{
> > +	if (enable) {
> > +		struct sigaction act;
> > +
> > +		SIG_ASSERT(igt_ioctl == sig_ioctl);
> > +		SIG_ASSERT(__igt_sigiter.tid == gettid());
> > +		igt_ioctl = drmIoctl;
> > +
> > +		timer_delete(__igt_sigiter.timer);
> > +
> > +		memset(&act, 0, sizeof(act));
> > +		act.sa_handler = SIG_IGN;
> > +		sigaction(SIGRTMIN, &act, NULL);
> > +
> > +		memset(&__igt_sigiter, 0, sizeof(__igt_sigiter));
> > +	}
> > +
> > +	memset(iter, 0, sizeof(*iter));
> > +	return false;
> > +}
> > +
> > +bool __igt_sigiter_continue(struct __igt_sigiter *iter, bool enable)
> > +{
> > +	if (iter->pass++ == 0)
> > +		return igt_sigiter_start(iter, enable);
> > +
> > +	/* If nothing reported SIGINT, nothing will on the next pass, so
> > +	 * give up! Also give up if everything is now executing faster
> > +	 * than current sigtimer.
> > +	 */
> > +	if (__igt_sigiter.stat.hit == 0 ||
> > +	    __igt_sigiter.stat.miss == __igt_sigiter.stat.ioctls)
> > +		return igt_sigiter_stop(iter, enable);
> > +
> > +	igt_debug("%s: pass %d, missed %ld/%ld\n",
> > +		  __func__, iter->pass - 1,
> > +		  __igt_sigiter.stat.miss,
> > +		  __igt_sigiter.stat.ioctls);
> > +
> > +	SIG_ASSERT(igt_ioctl == sig_ioctl);
> > +	SIG_ASSERT(__igt_sigiter.timer);
> > +
> > +	__igt_sigiter.offset.tv_sec *= 2;
> > +	__igt_sigiter.offset.tv_nsec *= 2;
> > +	while (__igt_sigiter.offset.tv_nsec >= NSEC_PER_SEC) {
> > +		__igt_sigiter.offset.tv_nsec -= NSEC_PER_SEC;
> > +		__igt_sigiter.offset.tv_sec += 1;
> > +	}
> > +	SIG_ASSERT(__igt_sigiter.offset.tv_nsec >= 0);
> > +	SIG_ASSERT(__igt_sigiter.offset.tv_sec >= 0);
> > +
> > +	memset(&__igt_sigiter.stat, 0, sizeof(__igt_sigiter.stat));
> > +	return true;
> > +}
> > +
> > +static struct igt_helper_process signal_helper;
> > +long long int sig_stat;
> > +__noreturn static void signal_helper_process(pid_t pid)
> > +{
> > +	/* Interrupt the parent process at 500Hz, just to be annoying */
> > +	while (1) {
> > +		usleep(1000 * 1000 / 500);
> > +		if (kill(pid, SIGCONT)) /* Parent has died, so must we. */
> > +			exit(0);
> > +	}
> > +}
> > +
> > +static void sig_handler(int i)
> > +{
> > +	sig_stat++;
> > +}
> > +
> > +/**
> > + * igt_fork_signal_helper:
> > + *
> > + * Fork a child process using #igt_fork_helper to interrupt the parent process
> > + * with a SIGCONT signal at regular quick intervals. The corresponding dummy
> > + * signal handler is installed in the parent process.
> > + *
> > + * This is useful to exercise ioctl error paths, at least where those can be
> > + * exercises by interrupting blocking waits, like stalling for the gpu. This
> > + * helper can also be used from children spawned with #igt_fork.
> > + *
> > + * In tests with subtests this function can be called outside of failure
> > + * catching code blocks like #igt_fixture or #igt_subtest.
> > + *
> > + * Note that this just spews signals at the current process unconditionally and
> > + * hence incurs quite a bit of overhead. For a more focused approach, with less
> > + * overhead, look at the #igt_while_interruptible code block macro.
> > + */
> > +void igt_fork_signal_helper(void)
> > +{
> > +	if (igt_only_list_subtests())
> > +		return;
> > +
> > +	/* We pick SIGCONT as it is a "safe" signal - if we send SIGCONT to
> > +	 * an unexpecting process it spuriously wakes up and does nothing.
> > +	 * Most other signals (e.g. SIGUSR1) cause the process to die if they
> > +	 * are not handled. This is an issue in case the sighandler is not
> > +	 * inherited correctly (or if there is a race in the inheritance
> > +	 * and we send the signal at exactly the wrong time).
> > +	 */
> > +	signal(SIGCONT, sig_handler);
> > +	setpgid(0, 0); /* define a new process group for the tests */
> > +
> > +	igt_fork_helper(&signal_helper) {
> > +		setpgid(0, 0); /* Escape from the test process group */
> > +
> > +		/* Pass along the test process group identifier,
> > +		 * negative pid => send signal to everyone in the group.
> > +		 */
> > +		signal_helper_process(-getppid());
> > +	}
> > +}
> > +
> > +/**
> > + * igt_stop_signal_helper:
> > + *
> > + * Stops the child process spawned with igt_fork_signal_helper() again.
> > + *
> > + * In tests with subtests this function can be called outside of failure
> > + * catching code blocks like #igt_fixture or #igt_subtest.
> > + */
> > +void igt_stop_signal_helper(void)
> > +{
> > +	if (igt_only_list_subtests())
> > +		return;
> > +
> > +	igt_stop_helper(&signal_helper);
> > +
> > +	sig_stat = 0;
> > +}
> > +
> > +/**
> > + * igt_suspend_signal_helper:
> > + *
> > + * Suspends the child process spawned with igt_fork_signal_helper(). This
> > + * should be called before a critical section of code that has difficulty to
> > + * make progress if interrupted frequently, like the clone() syscall called
> > + * from a largish executable. igt_resume_signal_helper() must be called after
> > + * the critical section to restart interruptions for the test.
> > + */
> > +void igt_suspend_signal_helper(void)
> > +{
> > +	int status;
> > +
> > +	if (!signal_helper.running)
> > +		return;
> > +
> > +	kill(signal_helper.pid, SIGSTOP);
> > +	while (waitpid(signal_helper.pid, &status, WUNTRACED) == -1 &&
> > +	       errno == EINTR)
> > +		;
> > +}
> > +
> > +/**
> > + * igt_resume_signal_helper:
> > + *
> > + * Resumes the child process spawned with igt_fork_signal_helper().
> > + *
> > + * This should be paired with igt_suspend_signal_helper() and called after the
> > + * problematic code sensitive to signals.
> > + */
> > +void igt_resume_signal_helper(void)
> > +{
> > +	if (!signal_helper.running)
> > +		return;
> > +
> > +	kill(signal_helper.pid, SIGCONT);
> > +}
> > +
> > +static struct igt_helper_process shrink_helper;
> > +__noreturn static void shrink_helper_process(int fd, pid_t pid)
> > +{
> > +	while (1) {
> > +		igt_drop_caches_set(fd, DROP_SHRINK_ALL);
> > +		usleep(1000 * 1000 / 50);
> > +		if (kill(pid, 0)) /* Parent has died, so must we. */
> > +			exit(0);
> > +	}
> > +}
> > +
> > +/**
> > + * igt_fork_shrink_helper:
> > + *
> > + * Fork a child process using #igt_fork_helper to force all available objects
> > + * to be paged out (via i915_gem_shrink()).
> > + *
> > + * This is useful to exercise swapping paths, without requiring us to hit swap.
> > + *
> > + * This should only be used from an igt_fixture.
> > + */
> > +void igt_fork_shrink_helper(int drm_fd)
> > +{
> > +	assert(!igt_only_list_subtests());
> > +	igt_require(igt_drop_caches_has(drm_fd, DROP_SHRINK_ALL));
> > +	igt_fork_helper(&shrink_helper)
> > +		shrink_helper_process(drm_fd, getppid());
> > +}
> > +
> > +/**
> > + * igt_stop_shrink_helper:
> > + *
> > + * Stops the child process spawned with igt_fork_shrink_helper().
> > + */
> > +void igt_stop_shrink_helper(void)
> > +{
> > +	igt_stop_helper(&shrink_helper);
> > +}
> > +
> > +static void show_kernel_stack(pid_t pid)
> > +{
> > +	char buf[80], *str;
> > +	int dir;
> > +
> > +	snprintf(buf, sizeof(buf), "/proc/%d", pid);
> > +	dir = open(buf, O_RDONLY);
> > +	if (dir < 0)
> > +		return;
> > +
> > +	str = igt_sysfs_get(dir, "stack");
> > +	if (str) {
> > +		igt_debug("Kernel stack for pid %d:\n%s\n", pid, str);
> > +		free(str);
> > +	}
> > +
> > +	close(dir);
> > +}
> > +
> > +static struct igt_helper_process hang_detector;
> > +__noreturn static void
> > +hang_detector_process(int fd, pid_t pid, dev_t rdev)
> > +{
> > +	struct udev_monitor *mon =
> > +		udev_monitor_new_from_netlink(udev_new(), "kernel");
> > +	struct pollfd pfd;
> > +	int ret;
> > +
> > +	udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", NULL);
> > +	udev_monitor_enable_receiving(mon);
> > +
> > +	pfd.fd = udev_monitor_get_fd(mon);
> > +	pfd.events = POLLIN;
> > +
> > +	while ((ret = poll(&pfd, 1, 2000)) >= 0) {
> > +		struct udev_device *dev;
> > +		dev_t devnum;
> > +
> > +		if (kill(pid, 0)) { /* Parent has died, so must we. */
> > +			igt_warn("Parent died without killing its children (%s)\n",
> > +				 __func__);
> > +			break;
> > +		}
> > +
> > +		dev = NULL;
> > +		if (ret > 0)
> > +			dev = udev_monitor_receive_device(mon);
> > +		if (dev == NULL)
> > +			continue;
> > +
> > +		devnum = udev_device_get_devnum(dev);
> > +		if (memcmp(&rdev, &devnum, sizeof(dev_t)) == 0) {
> > +			const char *str;
> > +
> > +			str = udev_device_get_property_value(dev, "ERROR");
> > +			if (str && atoi(str) == 1) {
> > +				show_kernel_stack(pid);
> > +				kill(pid, SIGIO);
> > +			}
> > +		}
> > +
> > +		udev_device_unref(dev);
> > +	}
> > +
> > +	exit(0);
> > +}
> > +
> > +__noreturn static void sig_abort(int sig)
> > +{
> > +	errno = 0; /* inside a signal, last errno reporting is confusing */
> > +	igt_assert(!"GPU hung");
> > +}
> > +
> > +void igt_fork_hang_detector(int fd)
> > +{
> > +	struct stat st;
> > +
> > +	igt_assert(fstat(fd, &st) == 0);
> > +
> > +	/*
> > +	 * Disable per-engine reset to force an error uevent. We don't
> > +	 * expect to get any hangs whilst the detector is enabled (if we do
> > +	 * they are a test failure!) and so the loss of per-engine reset
> > +	 * functionality is not an issue.
> > +	 */
> > +	igt_assert(igt_params_set(fd, "reset", "%d", 1 /* only global reset */));
> > +
> > +	signal(SIGIO, sig_abort);
> > +	igt_fork_helper(&hang_detector)
> > +		hang_detector_process(fd, getppid(), st.st_rdev);
> > +}
> > +
> > +void igt_stop_hang_detector(void)
> > +{
> > +	/*
> > +	 * Give the uevent time to arrive. No sleep at all misses about 20% of
> > +	 * hangs (at least, in the i915_hangman/detector test). A sleep of 1ms
> > +	 * seems to miss about 2%, 10ms loses <1%, so 100ms should be safe.
> > +	 */
> > +	usleep(100 * 1000);
> > +
> > +	igt_stop_helper(&hang_detector);
> > +}
> > +
> > +/**
> > + * igt_check_boolean_env_var:
> > + * @env_var: environment variable name
> > + * @default_value: default value for the environment variable
> > + *
> > + * This function should be used to parse boolean environment variable options.
> > + *
> > + * Returns:
> > + * The boolean value of the environment variable @env_var as decoded by atoi()
> > + * if it is set and @default_value if the variable is not set.
> > + */
> > +bool igt_check_boolean_env_var(const char *env_var, bool default_value)
> > +{
> > +	char *val;
> > +
> > +	val = getenv(env_var);
> > +	if (!val)
> > +		return default_value;
> > +
> > +	return atoi(val) != 0;
> > +}
> > +
> > +/**
> > + * igt_aub_dump_enabled:
> > + *
> > + * Returns:
> > + * True if AUB dumping is enabled with IGT_DUMP_AUB=1 in the environment, false
> > + * otherwise.
> > + */
> > +bool igt_aub_dump_enabled(void)
> > +{
> > +	static int dump_aub = -1;
> > +
> > +	if (dump_aub == -1)
> > +		dump_aub = igt_check_boolean_env_var("IGT_DUMP_AUB", false);
> > +
> > +	return dump_aub;
> > +}
> > +
> > +/* other helpers */
> > +/**
> > + * igt_exchange_int:
> > + * @array: pointer to the array of integers
> > + * @i: first position
> > + * @j: second position
> > + *
> > + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> > + * function for igt_permute_array().
> > + */
> > +void igt_exchange_int(void *array, unsigned i, unsigned j)
> > +{
> > +	int *int_arr, tmp;
> > +	int_arr = array;
> > +
> > +	tmp = int_arr[i];
> > +	int_arr[i] = int_arr[j];
> > +	int_arr[j] = tmp;
> > +}
> > +
> > +/**
> > + * igt_exchange_int64:
> > + * @array: pointer to the array of int64_t
> > + * @i: first position
> > + * @j: second position
> > + *
> > + * Exchanges the two values at array indices @i and @j. Useful as an exchange
> > + * function for igt_permute_array().
> > + */
> > +void igt_exchange_int64(void *array, unsigned i, unsigned j)
> > +{
> > +	int64_t *a = array;
> > +
> > +	igt_swap(a[i], a[j]);
> > +}
> > +
> > +/**
> > + * igt_permute_array:
> > + * @array: pointer to array
> > + * @size: size of the array
> > + * @exchange_func: function to exchange array elements
> > + *
> > + * This function randomly permutes the array using random() as the PRNG source.
> > + * The @exchange_func function is called to exchange two elements in the array
> > + * when needed.
> > + */
> > +void igt_permute_array(void *array, unsigned size,
> > +                       void (*exchange_func)(void *array,
> > +                                             unsigned i,
> > +                                             unsigned j))
> > +{
> > +	int i;
> > +
> > +	for (i = size - 1; i > 0; i--) {
> > +		/* yes, not perfectly uniform, who cares */
> > +		long l = hars_petruska_f54_1_random_unsafe() % (i +1);
> > +		if (i != l)
> > +			exchange_func(array, i, l);
> > +	}
> > +}
> > +
> > +__attribute__((format(printf, 1, 2)))
> > +static void igt_interactive_info(const char *format, ...)
> > +{
> > +	va_list args;
> > +
> > +	if (!isatty(STDERR_FILENO) || __igt_plain_output) {
> > +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> > +		return;
> > +	}
> > +
> > +	if (igt_log_level > IGT_LOG_INFO)
> > +		return;
> > +
> > +	va_start(args, format);
> > +	vfprintf(stderr, format, args);
> > +	va_end(args);
> > +}
> > +
> > +
> > +/**
> > + * igt_progress:
> > + * @header: header string to prepend to the progress indicator
> > + * @i: work processed thus far
> > + * @total: total amount of work
> > + *
> > + * This function draws a progress indicator, which is useful for running
> > + * long-winded tests manually on the console. To avoid spamming log files in
> > + * automated runs the progress indicator is suppressed when not running on a
> > + * terminal.
> > + */
> > +void igt_progress(const char *header, uint64_t i, uint64_t total)
> > +{
> > +	int divider = 200;
> > +
> > +	if (i+1 >= total) {
> > +		igt_interactive_info("\r%s100%%\n", header);
> > +		return;
> > +	}
> > +
> > +	if (total / 200 == 0)
> > +		divider = 1;
> > +
> > +	/* only bother updating about every 0.5% */
> > +	if (i % (total / divider) == 0)
> > +		igt_interactive_info("\r%s%3llu%%", header,
> > +				     (long long unsigned)i * 100 / total);
> > +}
> > +
> > +/**
> > + * igt_print_activity:
> > + *
> > + * Print a '.' to indicate activity. This is printed without a newline and
> > + * only if output is to a terminal.
> > + */
> > +void igt_print_activity(void)
> > +{
> > +	igt_interactive_info(".");
> > +}
> > +
> > +static int autoresume_delay;
> > +
> > +static const char *suspend_state_name[] = {
> > +	[SUSPEND_STATE_FREEZE] = "freeze",
> > +	[SUSPEND_STATE_STANDBY] = "standby",
> > +	[SUSPEND_STATE_S3] = "mem", /* Forces Suspend-to-Ram (S3) */
> > +	[SUSPEND_STATE_MEM] = "mem", /* Respects system default */
> > +	[SUSPEND_STATE_DISK] = "disk",
> > +};
> > +
> > +static const char *suspend_test_name[] = {
> > +	[SUSPEND_TEST_NONE] = "none",
> > +	[SUSPEND_TEST_FREEZER] = "freezer",
> > +	[SUSPEND_TEST_DEVICES] = "devices",
> > +	[SUSPEND_TEST_PLATFORM] = "platform",
> > +	[SUSPEND_TEST_PROCESSORS] = "processors",
> > +	[SUSPEND_TEST_CORE] = "core",
> > +};
> > +
> > +static const char *mem_sleep_name[] = {
> > +	[MEM_SLEEP_S2IDLE] = "s2idle",
> > +	[MEM_SLEEP_SHALLOW] = "shallow",
> > +	[MEM_SLEEP_DEEP] = "deep"
> > +};
> > +
> > +static enum igt_suspend_test get_suspend_test(int power_dir)
> > +{
> > +	char *test_line;
> > +	char *test_name;
> > +	enum igt_suspend_test test;
> > +
> > +	if (faccessat(power_dir, "pm_test", R_OK, 0))
> > +		return SUSPEND_TEST_NONE;
> > +
> > +	igt_assert((test_line = igt_sysfs_get(power_dir, "pm_test")));
> > +	for (test_name = strtok(test_line, " "); test_name;
> > +	     test_name = strtok(NULL, " "))
> > +		if (test_name[0] == '[') {
> > +			test_name[strlen(test_name) - 1] = '\0';
> > +			test_name++;
> > +			break;
> > +		}
> > +
> > +	if (!test_name) {
> > +	  	free(test_line);
> > +		return SUSPEND_TEST_NONE;
> > +	}
> > +
> > +	for (test = SUSPEND_TEST_NONE; test < SUSPEND_TEST_NUM; test++)
> > +		if (strcmp(suspend_test_name[test], test_name) == 0)
> > +			break;
> > +
> > +	igt_assert(test < SUSPEND_TEST_NUM);
> > +
> > +	free(test_line);
> > +	return test;
> > +}
> > +
> > +static void set_suspend_test(int power_dir, enum igt_suspend_test test)
> > +{
> > +	igt_assert(test < SUSPEND_TEST_NUM);
> > +
> > +	if (faccessat(power_dir, "pm_test", W_OK, 0)) {
> > +		igt_require(test == SUSPEND_TEST_NONE);
> > +		return;
> > +	}
> > +
> > +	igt_assert(igt_sysfs_set(power_dir, "pm_test", suspend_test_name[test]));
> > +}
> > +
> > +#define SQUELCH ">/dev/null 2>&1"
> > +
> > +static void suspend_via_rtcwake(enum igt_suspend_state state)
> > +{
> > +	char cmd[128];
> > +	int delay, ret;
> > +
> > +	igt_assert(state < SUSPEND_STATE_NUM);
> > +
> > +	delay = igt_get_autoresume_delay(state);
> > +
> > +	/*
> > +	 * Skip if rtcwake would fail for a reason not related to the kernel's
> > +	 * suspend functionality.
> > +	 */
> > +	snprintf(cmd, sizeof(cmd), "rtcwake -n -s %d -m %s " SQUELCH,
> > +		 delay, suspend_state_name[state]);
> > +	ret = igt_system(cmd);
> > +	igt_require_f(ret == 0, "rtcwake test failed with %i\n"
> > +		     "This failure could mean that something is wrong with "
> > +		     "the rtcwake tool or how your distro is set up.\n",
> > +		      ret);
> > +
> > +	snprintf(cmd, sizeof(cmd), "rtcwake -s %d -m %s ",
> > +		 delay, suspend_state_name[state]);
> > +	ret = igt_system(cmd);
> > +	if (ret) {
> > +		const char *path = "suspend_stats";
> > +		char *info;
> > +		int dir;
> > +
> > +		igt_warn("rtcwake failed with %i\n"
> > +			 "Check dmesg for further details.\n",
> > +			 ret);
> > +
> > +		dir = open(igt_debugfs_mount(), O_RDONLY);
> > +		info = igt_sysfs_get(dir, path);
> > +		close(dir);
> > +		if (info) {
> > +			igt_debug("%s:\n%s\n", path, info);
> > +			free(info);
> > +		}
> > +	}
> > +	igt_assert_eq(ret, 0);
> > +}
> > +
> > +static void suspend_via_sysfs(int power_dir, enum igt_suspend_state state)
> > +{
> > +	igt_assert(state < SUSPEND_STATE_NUM);
> > +	igt_assert(igt_sysfs_set(power_dir, "state",
> > +				 suspend_state_name[state]));
> > +}
> > +
> > +static bool is_state_supported(int power_dir, enum igt_suspend_state state)
> > +{
> > +	const char *str;
> > +	char *states;
> > +
> > +	igt_assert((states = igt_sysfs_get(power_dir, "state")));
> > +
> > +	str = strstr(states, suspend_state_name[state]);
> > +
> > +	if (!str)
> > +		igt_info("State %s not supported.\nSupported States: %s\n",
> > +			 suspend_state_name[state], states);
> > +
> > +	free(states);
> > +	return str;
> > +}
> > +
> > +static int get_mem_sleep(void)
> > +{
> > +	char *mem_sleep_states;
> > +	char *mem_sleep_state;
> > +	enum igt_mem_sleep mem_sleep;
> > +	int power_dir;
> > +
> > +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> > +
> > +	if (faccessat(power_dir, "mem_sleep", R_OK, 0))
> > +		return MEM_SLEEP_NONE;
> > +
> > +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> > +	for (mem_sleep_state = strtok(mem_sleep_states, " "); mem_sleep_state;
> > +	     mem_sleep_state = strtok(NULL, " ")) {
> > +		if (mem_sleep_state[0] == '[') {
> > +			mem_sleep_state[strlen(mem_sleep_state) - 1] = '\0';
> > +			mem_sleep_state++;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (!mem_sleep_state) {
> > +		free(mem_sleep_states);
> > +		return MEM_SLEEP_NONE;
> > +	}
> > +
> > +	for (mem_sleep = MEM_SLEEP_S2IDLE; mem_sleep < MEM_SLEEP_NUM; mem_sleep++) {
> > +		if (strcmp(mem_sleep_name[mem_sleep], mem_sleep_state) == 0)
> > +			break;
> > +	}
> > +
> > +	igt_assert_f(mem_sleep < MEM_SLEEP_NUM, "Invalid mem_sleep state\n");
> > +
> > +	free(mem_sleep_states);
> > +	close(power_dir);
> > +	return mem_sleep;
> > +}
> > +
> > +static void set_mem_sleep(int power_dir, enum igt_mem_sleep sleep)
> > +{
> > +	igt_assert(sleep < MEM_SLEEP_NUM);
> > +
> > +	igt_assert_eq(faccessat(power_dir, "mem_sleep", W_OK, 0), 0);
> > +
> > +	igt_assert(igt_sysfs_set(power_dir, "mem_sleep",
> > +				 mem_sleep_name[sleep]));
> > +}
> > +
> > +static bool is_mem_sleep_state_supported(int power_dir, enum igt_mem_sleep state)
> > +{
> > +	const char *str;
> > +	char *mem_sleep_states;
> > +
> > +	igt_assert((mem_sleep_states = igt_sysfs_get(power_dir, "mem_sleep")));
> > +
> > +	str = strstr(mem_sleep_states, mem_sleep_name[state]);
> > +
> > +	if (!str)
> > +		igt_info("mem_sleep state %s not supported.\nSupported mem_sleep states: %s\n",
> > +			 mem_sleep_name[state], mem_sleep_states);
> > +
> > +	free(mem_sleep_states);
> > +	return str;
> > +}
> > +
> > +/**
> > + * igt_system_suspend_autoresume:
> > + * @state: an #igt_suspend_state, the target suspend state
> > + * @test: an #igt_suspend_test, test point at which to complete the suspend
> > + *	  cycle
> > + *
> > + * Execute a system suspend cycle targeting the given @state optionally
> > + * completing the cycle at the given @test point and automaically wake up
> > + * again. Waking up is either achieved using the RTC wake-up alarm for a full
> > + * suspend cycle or a kernel timer for a suspend test cycle. The kernel timer
> > + * delay for a test cycle can be configured by the suspend.pm_test_delay
> > + * kernel parameter (5 sec by default).
> > + *
> > + * #SUSPEND_TEST_NONE specifies a full suspend cycle.
> > + * The #SUSPEND_TEST_FREEZER..#SUSPEND_TEST_CORE test points can make it
> > + * possible to collect error logs in case a full suspend cycle would prevent
> > + * this by hanging the machine, or they can provide an idea of the faulty
> > + * component by comparing fail/no-fail results at different test points.
> > + *
> > + * This is very handy for implementing any kind of suspend/resume test.
> > + */
> > +void igt_system_suspend_autoresume(enum igt_suspend_state state,
> > +				   enum igt_suspend_test test)
> > +{
> > +	int power_dir;
> > +	enum igt_suspend_test orig_test;
> > +	enum igt_mem_sleep orig_mem_sleep = MEM_SLEEP_NONE;
> > +
> > +	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
> > +	igt_require(is_state_supported(power_dir, state));
> > +	igt_require(test == SUSPEND_TEST_NONE ||
> > +		    faccessat(power_dir, "pm_test", R_OK | W_OK, 0) == 0);
> > +
> > +	igt_skip_on_f(state == SUSPEND_STATE_DISK &&
> > +		      !igt_get_total_swap_mb(),
> > +		      "Suspend to disk requires swap space.\n");
> > +
> > +	orig_test = get_suspend_test(power_dir);
> > +
> > +	if (state == SUSPEND_STATE_S3) {
> > +		orig_mem_sleep = get_mem_sleep();
> > +		igt_skip_on_f(!is_mem_sleep_state_supported(power_dir, MEM_SLEEP_DEEP),
> > +			      "S3 not supported in this system.\n");
> > +		set_mem_sleep(power_dir, MEM_SLEEP_DEEP);
> > +		igt_skip_on_f(get_mem_sleep() != MEM_SLEEP_DEEP,
> > +			      "S3 not possible in this system.\n");
> > +	}
> > +
> > +	set_suspend_test(power_dir, test);
> > +
> > +	if (test == SUSPEND_TEST_NONE)
> > +		suspend_via_rtcwake(state);
> > +	else
> > +		suspend_via_sysfs(power_dir, state);
> > +
> > +	if (orig_mem_sleep)
> > +		set_mem_sleep(power_dir, orig_mem_sleep);
> > +
> > +	set_suspend_test(power_dir, orig_test);
> > +	close(power_dir);
> > +}
> > +
> > +static int original_autoresume_delay;
> > +
> > +static void igt_restore_autoresume_delay(int sig)
> > +{
> > +	int delay_fd;
> > +	char delay_str[10];
> > +
> > +	igt_require((delay_fd = open("/sys/module/suspend/parameters/pm_test_delay",
> > +				    O_WRONLY)) >= 0);
> > +
> > +	snprintf(delay_str, sizeof(delay_str), "%d", original_autoresume_delay);
> > +	igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> > +
> > +	close(delay_fd);
> > +}
> > +
> > +/**
> > + * igt_set_autoresume_delay:
> > + * @delay_secs: The delay in seconds before resuming the system
> > + *
> > + * Sets how long we wait to resume the system after suspending it, using the
> > + * suspend.pm_test_delay variable. On exit, the original delay value is
> > + * restored.
> > + */
> > +void igt_set_autoresume_delay(int delay_secs)
> > +{
> > +	int delay_fd;
> > +	char delay_str[10];
> > +
> > +	delay_fd = open("/sys/module/suspend/parameters/pm_test_delay", O_RDWR);
> > +
> > +	if (delay_fd >= 0) {
> > +		if (!original_autoresume_delay) {
> > +			igt_require(read(delay_fd, delay_str,
> > +					 sizeof(delay_str)));
> > +			original_autoresume_delay = atoi(delay_str);
> > +			igt_install_exit_handler(igt_restore_autoresume_delay);
> > +		}
> > +
> > +		snprintf(delay_str, sizeof(delay_str), "%d", delay_secs);
> > +		igt_require(write(delay_fd, delay_str, strlen(delay_str)));
> > +
> > +		close(delay_fd);
> > +	}
> > +
> > +	autoresume_delay = delay_secs;
> > +}
> > +
> > +/**
> > + * igt_get_autoresume_delay:
> > + * @state: an #igt_suspend_state, the target suspend state
> > + *
> > + * Retrieves how long we wait to resume the system after suspending it.
> > + * This can either be set through igt_set_autoresume_delay or be a default
> > + * value that depends on the suspend state.
> > + *
> > + * Returns: The autoresume delay, in seconds.
> > + */
> > +int igt_get_autoresume_delay(enum igt_suspend_state state)
> > +{
> > +	int delay;
> > +
> > +	if (autoresume_delay)
> > +		delay = autoresume_delay;
> > +	else
> > +		delay = state == SUSPEND_STATE_DISK ? 30 : 15;
> > +
> > +	return delay;
> > +}
> > +
> > +/**
> > + * igt_drop_root:
> > + *
> > + * Drop root privileges and make sure it actually worked. Useful for tests
> > + * which need to check security constraints. Note that this should only be
> > + * called from manually forked processes, since the lack of root privileges
> > + * will wreak havoc with the automatic cleanup handlers.
> > + */
> > +void igt_drop_root(void)
> > +{
> > +	igt_assert_eq(getuid(), 0);
> > +
> > +	igt_assert_eq(setgroups(0, NULL), 0);
> > +	igt_assert_eq(setgid(2), 0);
> > +	igt_assert_eq(setuid(2), 0);
> > +
> > +	igt_assert_eq(getgroups(0, NULL), 0);
> > +	igt_assert_eq(getgid(), 2);
> > +	igt_assert_eq(getuid(), 2);
> > +}
> > +
> > +/**
> > + * igt_debug_wait_for_keypress:
> > + * @var: var lookup to to enable this wait
> > + *
> > + * Waits for a key press when run interactively and when the corresponding debug
> > + * var is set in the --interactive-debug=$var variable. Multiple keys
> > + * can be specified as a comma-separated list or alternatively "all" if a wait
> > + * should happen for all cases. Calling this function with "all" will assert.
> > + *
> > + * When not connected to a terminal interactive_debug is ignored
> > + * and execution immediately continues.
> > + *
> > + * This is useful for display tests where under certain situation manual
> > + * inspection of the display is useful. Or when running a testcase in the
> > + * background.
> > + */
> > +void igt_debug_wait_for_keypress(const char *var)
> > +{
> > +	struct termios oldt, newt;
> > +
> > +	if (!isatty(STDIN_FILENO)) {
> > +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> > +		return;
> > +	}
> > +
> > +	if (!igt_interactive_debug)
> > +		return;
> > +
> > +	if (strstr(var, "all"))
> > +		igt_assert_f(false, "Bug in test: Do not call igt_debug_wait_for_keypress with \"all\"\n");
> > +
> > +	if (!strstr(igt_interactive_debug, var) &&
> > +	    !strstr(igt_interactive_debug, "all"))
> > +		return;
> > +
> > +	igt_info("Press any key to continue ...\n");
> > +
> > +	tcgetattr ( STDIN_FILENO, &oldt );
> > +	newt = oldt;
> > +	newt.c_lflag &= ~( ICANON | ECHO );
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> > +	getchar();
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> > +}
> > +
> > +/**
> > + * igt_debug_interactive_mode_check:
> > + * @var: var lookup to to enable this wait
> > + * @expected: message to be printed as expected behaviour before wait for keys Y/n
> > + *
> > + * Waits for a key press when run interactively and when the corresponding debug
> > + * var is set in the --interactive-debug=$var variable. Multiple vars
> > + * can be specified as a comma-separated list or alternatively "all" if a wait
> > + * should happen for all cases.
> > + *
> > + * This is useful for display tests where under certain situation manual
> > + * inspection of the display is useful. Or when running a testcase in the
> > + * background.
> > + *
> > + * When not connected to a terminal interactive_debug is ignored
> > + * and execution immediately continues. For this reason by default this function
> > + * returns true. It returns false only when N/n is pressed indicating the
> > + * user isn't seeing what was expected.
> > + *
> > + * Force test fail when N/n is pressed.
> > + */
> > +void igt_debug_interactive_mode_check(const char *var, const char *expected)
> > +{
> > +	struct termios oldt, newt;
> > +	char key;
> > +
> > +	if (!isatty(STDIN_FILENO)) {
> > +		errno = 0; /* otherwise would be either ENOTTY or EBADF */
> > +		return;
> > +	}
> > +
> > +	if (!igt_interactive_debug)
> > +		return;
> > +
> > +	if (!strstr(igt_interactive_debug, var) &&
> > +	    !strstr(igt_interactive_debug, "all"))
> > +		return;
> > +
> > +	igt_info("Is %s [Y/n]", expected);
> > +
> > +	tcgetattr ( STDIN_FILENO, &oldt );
> > +	newt = oldt;
> > +	newt.c_lflag &= ~ICANON;
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
> > +	key = getchar();
> > +	tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
> > +
> > +	igt_info("\n");
> > +
> > +	igt_assert(key != 'n' && key != 'N');
> > +}
> > +
> > +/**
> > + * igt_lock_mem:
> > + * @size: the amount of memory to lock into RAM, in MB
> > + *
> > + * Allocate @size MB of memory and lock it into RAM. This releases any
> > + * previously locked memory.
> > + *
> > + * Use #igt_unlock_mem to release the currently locked memory.
> > + */
> > +static char *locked_mem;
> > +static size_t locked_size;
> > +
> > +void igt_lock_mem(size_t size)
> > +{
> > +	long pagesize = sysconf(_SC_PAGESIZE);
> > +	size_t i;
> > +	int ret;
> > +
> > +	if (size == 0) {
> > +		return;
> > +	}
> > +
> > +	if (locked_mem) {
> > +		igt_unlock_mem();
> > +		igt_warn("Unlocking previously locked memory.\n");
> > +	}
> > +
> > +	locked_size = size * 1024 * 1024;
> > +
> > +	locked_mem = malloc(locked_size);
> > +	igt_require_f(locked_mem,
> > +		      "Could not malloc %zdMiB for locking.\n", size);
> > +
> > +	/* write into each page to ensure it is allocated */
> > +	for (i = 0; i < locked_size; i += pagesize)
> > +		locked_mem[i] = i;
> > +
> > +	ret = mlock(locked_mem, locked_size);
> > +	igt_assert_f(ret == 0, "Could not mlock %zdMiB.\n", size);
> > +}
> > +
> > +/**
> > + * igt_unlock_mem:
> > + *
> > + * Release and free the RAM used by #igt_lock_mem.
> > + */
> > +void igt_unlock_mem(void)
> > +{
> > +	if (!locked_mem)
> > +		return;
> > +
> > +	munlock(locked_mem, locked_size);
> > +
> > +	free(locked_mem);
> > +	locked_mem = NULL;
> > +}
> > +
> > +/**
> > + * igt_is_process_running:
> > + * @comm: Name of process in the form found in /proc/pid/comm (limited to 15
> > + * chars)
> > + *
> > + * Returns: true in case the process has been found, false otherwise.
> > + *
> > + * This function checks in the process table for an entry with the name @comm.
> > + */
> > +int igt_is_process_running(const char *comm)
> > +{
> > +	PROCTAB *proc;
> > +	proc_t *proc_info;
> > +	bool found = false;
> > +
> > +	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT);
> > +	igt_assert(proc != NULL);
> > +
> > +	while ((proc_info = readproc(proc, NULL))) {
> > +		if (!strncasecmp(proc_info->cmd, comm, sizeof(proc_info->cmd))) {
> > +			freeproc(proc_info);
> > +			found = true;
> > +			break;
> > +		}
> > +		freeproc(proc_info);
> > +	}
> > +
> > +	closeproc(proc);
> > +	return found;
> > +}
> > +
> > +/**
> > + * igt_terminate_process:
> > + * @sig: Signal to send
> > + * @comm: Name of process in the form found in /proc/pid/comm (limited to 15
> > + * chars)
> > + *
> > + * Returns: 0 in case the process is not found running or the signal has been
> > + * sent successfully or -errno otherwise.
> > + *
> > + * This function sends the signal @sig for a process found in process table
> > + * with name @comm.
> > + */
> > +int igt_terminate_process(int sig, const char *comm)
> > +{
> > +	PROCTAB *proc;
> > +	proc_t *proc_info;
> > +	int err = 0;
> > +
> > +	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > +	igt_assert(proc != NULL);
> > +
> > +	while ((proc_info = readproc(proc, NULL))) {
> > +		if (!strncasecmp(proc_info->cmd, comm, sizeof(proc_info->cmd))) {
> > +
> > +			if (kill(proc_info->tid, sig) < 0)
> > +				err = -errno;
> > +
> > +			freeproc(proc_info);
> > +			break;
> > +		}
> > +		freeproc(proc_info);
> > +	}
> > +
> > +	closeproc(proc);
> > +	return err;
> > +}
> > +
> > +struct pinfo {
> > +	pid_t pid;
> > +	const char *comm;
> > +	const char *fn;
> > +};
> > +
> > +static void
> > +__igt_show_stat(struct pinfo *info)
> > +{
> > +	const char *comm, *fn;
> > +	const char *type = "";
> > +	struct stat st;
> > +
> > +	pid_t pid = info->pid;
> > +	igt_assert((comm = info->comm));
> > +	igt_assert((fn = info->fn));
> > +
> > +	if (lstat(fn, &st) == -1)
> > +		return;
> > +
> > +	igt_info("%20.20s ", comm);
> > +	igt_info("%10d ", pid);
> > +
> > +	switch (st.st_mode & S_IFMT) {
> > +	case S_IFBLK:
> > +		type = "block";
> > +		break;
> > +	case S_IFCHR:
> > +		type = "character";
> > +		break;
> > +	case S_IFDIR:
> > +		type = "directory";
> > +		break;
> > +	case S_IFIFO:
> > +		type = "FIFO/pipe";
> > +		break;
> > +	case S_IFLNK:
> > +		type = "symlink";
> > +		break;
> > +	case S_IFREG:
> > +		type = "file";
> > +		break;
> > +	case S_IFSOCK:
> > +		type = "socket";
> > +		break;
> > +	default:
> > +		type = "unknown?";
> > +		break;
> > +	}
> > +	igt_info("%20.20s ", type);
> > +
> > +	igt_info("%10ld%10ld ", (long) st.st_uid, (long) st.st_gid);
> > +
> > +	igt_info("%15lld bytes ", (long long) st.st_size);
> > +	igt_info("%30.30s", fn);
> > +	igt_info("\n");
> > +}
> > +
> > +
> > +static void
> > +igt_show_stat_header(void)
> > +{
> > +	igt_info("%20.20s%11.11s%21.21s%11.11s%10.10s%22.22s%31.31s\n",
> > +		"COMM", "PID", "Type", "UID", "GID", "Size", "Filename");
> > +}
> > +
> > +static void
> > +igt_show_stat(proc_t *info, int *state, const char *fn)
> > +{
> > +	struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
> > +
> > +	if (!*state)
> > +		igt_show_stat_header();
> > +
> > +	__igt_show_stat(&p);
> > +	++*state;
> > +}
> > +
> > +static void
> > +__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
> > +{
> > +	struct dirent *d;
> > +	struct stat st;
> > +	char path[PATH_MAX];
> > +	char *fd_lnk;
> > +
> > +	/* default fds or kernel threads */
> > +	const char *default_fds[] = { "/dev/pts", "/dev/null" };
> > +
> > +	DIR *dp = opendir(proc_path);
> > +	igt_assert(dp);
> > +again:
> > +	while ((d = readdir(dp))) {
> > +		char *copy_fd_lnk;
> > +		char *dirn;
> > +
> > +		unsigned int i;
> > +		ssize_t read;
> > +
> > +		if (*d->d_name == '.')
> > +			continue;
> > +
> > +		memset(path, 0, sizeof(path));
> > +		snprintf(path, sizeof(path), "%s/%s", proc_path, d->d_name);
> > +
> > +		if (lstat(path, &st) == -1)
> > +			continue;
> > +
> > +		fd_lnk = malloc(st.st_size + 1);
> > +
> > +		igt_assert((read = readlink(path, fd_lnk, st.st_size + 1)));
> > +		fd_lnk[read] = '\0';
> > +
> > +		for (i = 0; i < ARRAY_SIZE(default_fds); ++i) {
> > +			if (!strncmp(default_fds[i],
> > +				     fd_lnk,
> > +				     strlen(default_fds[i]))) {
> > +				free(fd_lnk);
> > +				goto again;
> > +			}
> > +		}
> > +
> > +		copy_fd_lnk = strdup(fd_lnk);
> > +		dirn = dirname(copy_fd_lnk);
> > +
> > +		if (!strncmp(dir, dirn, strlen(dir)))
> > +			igt_show_stat(proc_info, state, fd_lnk);
> > +
> > +		free(copy_fd_lnk);
> > +		free(fd_lnk);
> > +	}
> > +
> > +	closedir(dp);
> > +}
> > +
> > +/*
> > + * This functions verifies, for each process running on the machine, if the
> > + * current working directory or the fds matches the one supplied in dir.
> > + */
> > +static void
> > +__igt_lsof(const char *dir)
> > +{
> > +	PROCTAB *proc;
> > +	proc_t *proc_info;
> > +
> > +	char path[30];
> > +	char *name_lnk;
> > +	struct stat st;
> > +	int state = 0;
> > +
> > +	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > +	igt_assert(proc != NULL);
> > +
> > +	while ((proc_info = readproc(proc, NULL))) {
> > +		ssize_t read;
> > +
> > +		/* check current working directory */
> > +		memset(path, 0, sizeof(path));
> > +		snprintf(path, sizeof(path), "/proc/%d/cwd", proc_info->tid);
> > +
> > +		if (stat(path, &st) == -1)
> > +			continue;
> > +
> > +		name_lnk = malloc(st.st_size + 1);
> > +
> > +		igt_assert((read = readlink(path, name_lnk, st.st_size + 1)));
> > +		name_lnk[read] = '\0';
> > +
> > +		if (!strncmp(dir, name_lnk, strlen(dir)))
> > +			igt_show_stat(proc_info, &state, name_lnk);
> > +
> > +		/* check also fd, seems that lsof(8) doesn't look here */
> > +		memset(path, 0, sizeof(path));
> > +		snprintf(path, sizeof(path), "/proc/%d/fd", proc_info->tid);
> > +
> > +		__igt_lsof_fds(proc_info, &state, path, dir);
> > +
> > +		free(name_lnk);
> > +		freeproc(proc_info);
> > +	}
> > +
> > +	closeproc(proc);
> > +}
> > +
> > +/**
> > + * igt_lsof: Lists information about files opened by processes.
> > + * @dpath: Path to look under. A valid directory is required.
> > + *
> > + * This function mimics (a restrictive form of) lsof(8), but also shows
> > + * information about opened fds.
> > + */
> > +void
> > +igt_lsof(const char *dpath)
> > +{
> > +	struct stat st;
> > +	size_t len = strlen(dpath);
> > +	char *sanitized;
> > +
> > +	if (stat(dpath, &st) == -1)
> > +		return;
> > +
> > +	if (!S_ISDIR(st.st_mode)) {
> > +		igt_warn("%s not a directory!\n", dpath);
> > +		return;
> > +	}
> > +
> > +	sanitized = strdup(dpath);
> > +	/* remove last '/' so matching is easier */
> > +	if (len > 1 && dpath[len - 1] == '/')
> > +		sanitized[len - 1] = '\0';
> > +
> > +	__igt_lsof(sanitized);
> > +
> > +	free(sanitized);
> > +}
> > +
> > +static void pulseaudio_unload_module(proc_t *proc_info)
> > +{
> > +	struct igt_helper_process pa_proc = {};
> > +	char xdg_dir[PATH_MAX];
> > +	const char *homedir;
> > +	struct passwd *pw;
> > +
> > +	igt_fork_helper(&pa_proc) {
> > +		pw = getpwuid(proc_info->euid);
> > +		homedir = pw->pw_dir;
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > +
> > +		igt_info("Request pulseaudio to stop using audio device\n");
> > +
> > +		setgid(proc_info->egid);
> > +		setuid(proc_info->euid);
> > +		clearenv();
> > +		setenv("HOME", homedir, 1);
> > +		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> > +
> > +		system("for i in $(pacmd list-sources|grep module:|cut -d : -f 2); do pactl unload-module $i; done");
> > +	}
> > +	igt_wait_helper(&pa_proc);
> > +}
> > +
> > +static int pipewire_pulse_pid = 0;
> > +static int pipewire_pw_reserve_pid = 0;
> > +static struct igt_helper_process pw_reserve_proc = {};
> > +
> > +static void pipewire_reserve_wait(void)
> > +{
> > +	char xdg_dir[PATH_MAX];
> > +	const char *homedir;
> > +	struct passwd *pw;
> > +	proc_t *proc_info;
> > +	PROCTAB *proc;
> > +
> > +	igt_fork_helper(&pw_reserve_proc) {
> > +		igt_info("Preventing pipewire-pulse to use the audio drivers\n");
> > +
> > +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > +		igt_assert(proc != NULL);
> > +
> > +		while ((proc_info = readproc(proc, NULL))) {
> > +			if (pipewire_pulse_pid == proc_info->tid)
> > +				break;
> > +			freeproc(proc_info);
> > +		}
> > +		closeproc(proc);
> > +
> > +		/* Sanity check: if it can't find the process, it means it has gone */
> > +		if (pipewire_pulse_pid != proc_info->tid)
> > +			exit(0);
> > +
> > +		pw = getpwuid(proc_info->euid);
> > +		homedir = pw->pw_dir;
> > +		snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
> > +		setgid(proc_info->egid);
> > +		setuid(proc_info->euid);
> > +		clearenv();
> > +		setenv("HOME", homedir, 1);
> > +		setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
> > +		freeproc(proc_info);
> > +
> > +		/*
> > +		 * pw-reserve will run in background. It will only exit when
> > +		 * igt_kill_children() is called later on. So, it shouldn't
> > +		 * call igt_waitchildren(). Instead, just exit with the return
> > +		 * code from pw-reserve.
> > +		 */
> > +		exit(system("pw-reserve -n Audio0 -r"));
> > +	}
> > +}
> > +
> > +/* Maximum time waiting for pw-reserve to start running */
> > +#define PIPEWIRE_RESERVE_MAX_TIME 1000 /* milisseconds */
> > +
> > +int pipewire_pulse_start_reserve(void)
> > +{
> > +	bool is_pw_reserve_running = false;
> > +	proc_t *proc_info;
> > +	int attempts = 0;
> > +	PROCTAB *proc;
> > +
> > +	if (!pipewire_pulse_pid)
> > +		return 0;
> > +
> > +	pipewire_reserve_wait();
> > +
> > +	/*
> > +	 * Note: using pw-reserve to stop using audio only works with
> > +	 * pipewire version 0.3.50 or upper.
> > +	 */
> > +	for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
> > +		usleep(1000);
> > +		proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > +		igt_assert(proc != NULL);
> > +
> > +		while ((proc_info = readproc(proc, NULL))) {
> > +			if (!strcmp(proc_info->cmd, "pw-reserve")) {
> > +				is_pw_reserve_running = true;
> > +				pipewire_pw_reserve_pid = proc_info->tid;
> > +				freeproc(proc_info);
> > +				break;
> > +			}
> > +			freeproc(proc_info);
> > +		}
> > +		closeproc(proc);
> > +		if (is_pw_reserve_running)
> > +			break;
> > +	}
> > +	if (!is_pw_reserve_running) {
> > +		igt_warn("Failed to remove audio drivers from pipewire\n");
> > +		return 1;
> > +	}
> > +	/* Let's grant some time for pw_reserve to notify pipewire via D-BUS */
> > +	usleep(50000);
> > +
> > +	/*
> > +	 * pw-reserve is running, and should have stopped using the audio
> > +	 * drivers. We can now remove the driver.
> > +	 */
> > +
> > +	return 0;
> > +}
> > +
> > +void pipewire_pulse_stop_reserve(void)
> > +{
> > +	if (!pipewire_pulse_pid)
> > +		return;
> > +
> > +	igt_stop_helper(&pw_reserve_proc);
> > +}
> > +
> > +/**
> > + * __igt_lsof_audio_and_kill_proc() - check if a given process is using an
> > + *	audio device. If so, stop or prevent them to use such devices.
> > + *
> > + * @proc_info: process struct, as returned by readproc()
> > + * @proc_path: path of the process under procfs
> > + * @pipewire_pulse_pid: PID of pipewire-pulse process
> > + *
> > + * No processes can be using an audio device by the time it gets removed.
> > + * This function checks if a process is using an audio device from /dev/snd.
> > + * If so, it will check:
> > + * 	- if the process is pulseaudio, it can't be killed, as systemd will
> > + * 	  respawn it. So, instead, send a request for it to stop bind the
> > + * 	  audio devices.
> > + *	- if the process is pipewire-pulse, it can't be killed, as systemd will
> > + *	  respawn it. So, instead, the caller should call pw-reserve, remove
> > + *	  the kernel driver and then stop pw-reserve. On such case, this
> > + *	  function returns the PID of pipewire-pulse, but won't touch it.
> > + * If the check fails, it means that the process can simply be killed.
> > + */
> > +static int
> > +__igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
> > +{
> > +	const char *audio_dev = "/dev/snd/";
> > +	char path[PATH_MAX * 2];
> > +	struct dirent *d;
> > +	struct stat st;
> > +	char *fd_lnk;
> > +	int fail = 0;
> > +	ssize_t read;
> > +	DIR *dp;
> > +
> > +	/*
> > +	 * Terminating pipewire-pulse require an special procedure, which
> > +	 * is only available at version 0.3.50 and upper. Just trying to
> > +	 * kill pipewire will start a race between IGT and systemd. If IGT
> > +	 * wins, the audio driver will be unloaded before systemd tries to
> > +	 * reload it, but if systemd wins, the audio device will be re-opened
> > +	 * and used before IGT has a chance to remove the audio driver.
> > +	 * Pipewire version 0.3.50 should bring a standard way:
> > +	 *
> > +	 * 1) start a thread running:
> > +	 *	 pw-reserve -n Audio0 -r
> > +	 * 2) unload/unbind the the audio driver(s);
> > +	 * 3) stop the pw-reserve thread.
> > +	 */
> > +	if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
> > +		igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
> > +			 proc_info->tid, proc_info->cmd);
> > +		pipewire_pulse_pid = proc_info->tid;
> > +		return 0;
> > +	}
> > +	/*
> > +	 * pipewire-pulse itself doesn't hook into a /dev/snd device. Instead,
> > +	 * the actual binding happens at the Pipewire Session Manager, e.g.
> > +	 * either wireplumber or pipewire media-session.
> > +	 *
> > +	 * Just killing such processes won't produce any effect, as systemd
> > +	 * will respawn them. So, just ignore here, they'll honor pw-reserve,
> > +	 * when the time comes.
> > +	 */
> > +	if (!strcmp(proc_info->cmd, "pipewire-media-session"))
> > +		return 0;
> > +	if (!strcmp(proc_info->cmd, "wireplumber"))
> > +		return 0;
> > +
> > +	dp = opendir(proc_path);
> > +	if (!dp && errno == ENOENT)
> > +		return 0;
> > +	igt_assert(dp);
> > +
> > +	while ((d = readdir(dp))) {
> > +		if (*d->d_name == '.')
> > +			continue;
> > +
> > +		memset(path, 0, sizeof(path));
> > +		snprintf(path, sizeof(path), "%s/%s", proc_path, d->d_name);
> > +
> > +		if (lstat(path, &st) == -1)
> > +			continue;
> > +
> > +		fd_lnk = malloc(st.st_size + 1);
> > +
> > +		igt_assert((read = readlink(path, fd_lnk, st.st_size + 1)));
> > +		fd_lnk[read] = '\0';
> > +
> > +		if (strncmp(audio_dev, fd_lnk, strlen(audio_dev))) {
> > +			free(fd_lnk);
> > +			continue;
> > +		}
> > +
> > +		free(fd_lnk);
> > +
> > +		/*
> > +		 * In order to avoid racing against pa/systemd, ensure that
> > +		 * pulseaudio will close all audio files. This should be
> > +		 * enough to unbind audio modules and won't cause race issues
> > +		 * with systemd trying to reload it.
> > +		 */
> > +		if (!strcmp(proc_info->cmd, "pulseaudio")) {
> > +			pulseaudio_unload_module(proc_info);
> > +			break;
> > +		}
> > +
> > +		/* For all other processes, just kill them */
> > +		igt_info("process %d (%s) is using audio device. Should be terminated.\n",
> > +				proc_info->tid, proc_info->cmd);
> > +
> > +		if (kill(proc_info->tid, SIGTERM) < 0) {
> > +			igt_info("Fail to terminate %s (pid: %d) with SIGTERM\n",
> > +				proc_info->cmd, proc_info->tid);
> > +			if (kill(proc_info->tid, SIGABRT) < 0) {
> > +				fail++;
> > +				igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
> > +					proc_info->cmd, proc_info->tid);
> > +			}
> > +		}
> > +
> > +		break;
> > +	}
> > +
> > +	closedir(dp);
> > +	return fail;
> > +}
> > +
> > +/*
> > + * This function identifies each process running on the machine that is
> > + * opening an audio device and tries to stop it.
> > + *
> > + * Special care should be taken with pipewire and pipewire-pulse, as those
> > + * daemons are respanned if they got killed.
> > + */
> > +int
> > +igt_lsof_kill_audio_processes(void)
> > +{
> > +	char path[PATH_MAX];
> > +	proc_t *proc_info;
> > +	PROCTAB *proc;
> > +	int fail = 0;
> > +
> > +	proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
> > +	igt_assert(proc != NULL);
> > +	pipewire_pulse_pid = 0;
> > +
> > +	while ((proc_info = readproc(proc, NULL))) {
> > +		if (snprintf(path, sizeof(path), "/proc/%d/fd", proc_info->tid) < 1)
> > +			fail++;
> > +		else
> > +			fail += __igt_lsof_audio_and_kill_proc(proc_info, path);
> > +
> > +		freeproc(proc_info);
> > +	}
> > +	closeproc(proc);
> > +
> > +	return fail;
> > +}
> > +
> > +static struct igt_siglatency {
> > +	timer_t timer;
> > +	struct timespec target;
> > +	struct sigaction oldact;
> > +	struct igt_mean mean;
> > +
> > +	int sig;
> > +} igt_siglatency;
> > +
> > +static long delay(void)
> > +{
> > +	return hars_petruska_f54_1_random_unsafe() % (NSEC_PER_SEC / 1000);
> > +}
> > +
> > +static double elapsed(const struct timespec *now, const struct timespec *last)
> > +{
> > +	double nsecs;
> > +
> > +	nsecs = now->tv_nsec - last ->tv_nsec;
> > +	nsecs += 1e9*(now->tv_sec - last->tv_sec);
> > +
> > +	return nsecs;
> > +}
> > +
> > +static void siglatency(int sig, siginfo_t *info, void *arg)
> > +{
> > +	struct itimerspec its;
> > +
> > +	clock_gettime(CLOCK_MONOTONIC, &its.it_value);
> > +	if (info)
> > +		igt_mean_add(&igt_siglatency.mean,
> > +			     elapsed(&its.it_value, &igt_siglatency.target));
> > +	igt_siglatency.target = its.it_value;
> > +
> > +	its.it_value.tv_nsec += 100 * 1000;
> > +	its.it_value.tv_nsec += delay();
> > +	if (its.it_value.tv_nsec >= NSEC_PER_SEC) {
> > +		its.it_value.tv_nsec -= NSEC_PER_SEC;
> > +		its.it_value.tv_sec += 1;
> > +	}
> > +	its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
> > +	timer_settime(igt_siglatency.timer, TIMER_ABSTIME, &its, NULL);
> > +}
> > +
> > +void igt_start_siglatency(int sig)
> > +{
> > +	struct sigevent sev;
> > +	struct sigaction act;
> > +
> > +	if (sig <= 0)
> > +		sig = SIGRTMIN;
> > +
> > +	if (igt_siglatency.sig)
> > +		(void)igt_stop_siglatency(NULL);
> > +	igt_assert(igt_siglatency.sig == 0);
> > +	igt_siglatency.sig = sig;
> > +
> > +	memset(&sev, 0, sizeof(sev));
> > +	sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
> > +	sev.sigev_notify_thread_id = gettid();
> > +	sev.sigev_signo = sig;
> > +	timer_create(CLOCK_MONOTONIC, &sev, &igt_siglatency.timer);
> > +
> > +	memset(&act, 0, sizeof(act));
> > +	act.sa_sigaction = siglatency;
> > +	sigaction(sig, &act, &igt_siglatency.oldact);
> > +
> > +	siglatency(sig, NULL, NULL);
> > +}
> > +
> > +double igt_stop_siglatency(struct igt_mean *result)
> > +{
> > +	double mean = igt_mean_get(&igt_siglatency.mean);
> > +
> > +	if (result)
> > +		*result = igt_siglatency.mean;
> > +
> > +	sigaction(igt_siglatency.sig, &igt_siglatency.oldact, NULL);
> > +	timer_delete(igt_siglatency.timer);
> > +	memset(&igt_siglatency, 0, sizeof(igt_siglatency));
> > +
> > +	return mean;
> > +}
> > +
> > +bool igt_allow_unlimited_files(void)
> > +{
> > +	struct rlimit rlim;
> > +	unsigned nofile_rlim = 1024*1024;
> > +
> > +	FILE *file = fopen("/proc/sys/fs/nr_open", "r");
> > +	if (file) {
> > +		igt_assert(fscanf(file, "%u", &nofile_rlim) == 1);
> > +		igt_info("System limit for open files is %u\n", nofile_rlim);
> > +		fclose(file);
> > +	}
> > +
> > +	if (getrlimit(RLIMIT_NOFILE, &rlim))
> > +		return false;
> > +
> > +	rlim.rlim_cur = nofile_rlim;
> > +	rlim.rlim_max = nofile_rlim;
> > +	return setrlimit(RLIMIT_NOFILE, &rlim) == 0;
> > +}
> > +
> > +/**
> > + * vfs_file_max: report maximum number of files
> > + *
> > + * Get the global system-wide maximum of open files the kernel allows,
> > + * by reading /proc/sys/fs/file-max. Fails the current subtest if
> > + * reading the file fails, and returns a suitable best guess if it
> > + * cannot be opened.
> > + *
> > + * Returns: System-wide maximum of open files, or a best effort guess.
> > + */
> > +uint64_t vfs_file_max(void)
> > +{
> > +	static long long unsigned max;
> > +	if (max == 0) {
> > +		FILE *file = fopen("/proc/sys/fs/file-max", "r");
> > +		max = 80000;
> > +		if (file) {
> > +			igt_assert(fscanf(file, "%llu", &max) == 1);
> > +			fclose(file);
> > +		}
> > +	}
> > +	return max;
> > +}
> > +
> > +void *igt_memdup(const void *ptr, size_t len)
> > +{
> > +	void *dup;
> > +
> > +	dup = malloc(len);
> > +	if (dup)
> > +		memcpy(dup, ptr, len);
> > +
> > +	return dup;
> > +}
> > diff --git a/lib/meson.build b/lib/meson.build
> > index cef2d0ff..cb83ee99 100644
> > --- a/lib/meson.build
> > +++ b/lib/meson.build
> > @@ -103,7 +103,6 @@ lib_deps = [
> >  	libdrm,
> >  	libdw,
> >  	libkmod,
> > -	libprocps,
> >  	libudev,
> >  	math,
> >  	pciaccess,
> > @@ -173,6 +172,12 @@ if chamelium.found()
> >  	lib_sources += 'monitor_edids/monitor_edids_helper.c'
> >  endif
> >  
> > +if libprocps.found()
> > +	lib_deps += libprocps
> > +else
> > +	lib_deps += libproc2
> > +endif
> > +
> 
> If we want to use newer version maybe this should be other way:
> if libprocps2.found()
> 	lib_deps += libproc2
> else
> 	lib_deps += libprocps
> endif
> 
> Regards,
> Kamil
> 
> >  if get_option('srcdir') != ''
> >      srcdir = join_paths(get_option('srcdir'), 'tests')
> >  else
> > diff --git a/lib/meson.build.orig b/lib/meson.build.orig
> > new file mode 100644
> > index 00000000..cef2d0ff
> > --- /dev/null
> > +++ b/lib/meson.build.orig
> > @@ -0,0 +1,358 @@
> > +lib_sources = [
> > +	'drmtest.c',
> > +	'huc_copy.c',
> > +	'i915/gem.c',
> > +	'i915/gem_context.c',
> > +	'i915/gem_create.c',
> > +	'i915/gem_engine_topology.c',
> > +	'i915/gem_scheduler.c',
> > +	'i915/gem_submission.c',
> > +	'i915/gem_ring.c',
> > +	'i915/gem_mman.c',
> > +	'i915/gem_vm.c',
> > +	'i915/intel_memory_region.c',
> > +	'i915/intel_mocs.c',
> > +	'i915/i915_blt.c',
> > +	'i915/i915_crc.c',
> > +	'igt_collection.c',
> > +	'igt_color_encoding.c',
> > +	'igt_crc.c',
> > +	'igt_debugfs.c',
> > +	'igt_device.c',
> > +	'igt_device_scan.c',
> > +	'igt_drm_fdinfo.c',
> > +	'igt_aux.c',
> > +	'igt_gt.c',
> > +	'igt_halffloat.c',
> > +	'igt_hwmon.c',
> > +	'igt_io.c',
> > +	'igt_matrix.c',
> > +	'igt_os.c',
> > +	'igt_params.c',
> > +	'igt_perf.c',
> > +	'igt_pipe_crc.c',
> > +	'igt_power.c',
> > +	'igt_primes.c',
> > +	'igt_rand.c',
> > +	'igt_stats.c',
> > +	'igt_syncobj.c',
> > +	'igt_sysfs.c',
> > +	'igt_sysrq.c',
> > +	'igt_taints.c',
> > +	'igt_thread.c',
> > +	'igt_types.c',
> > +	'igt_vec.c',
> > +	'igt_vgem.c',
> > +	'igt_x86.c',
> > +	'instdone.c',
> > +	'intel_allocator.c',
> > +	'intel_allocator_msgchannel.c',
> > +	'intel_allocator_random.c',
> > +	'intel_allocator_reloc.c',
> > +	'intel_allocator_simple.c',
> > +	'intel_batchbuffer.c',
> > +	'intel_bufops.c',
> > +	'intel_chipset.c',
> > +	'intel_ctx.c',
> > +	'intel_device_info.c',
> > +	'intel_mmio.c',
> > +	'ioctl_wrappers.c',
> > +	'media_spin.c',
> > +	'media_fill.c',
> > +	'gpgpu_fill.c',
> > +	'gpu_cmds.c',
> > +	'rendercopy_i915.c',
> > +	'rendercopy_i830.c',
> > +	'rendercopy_gen4.c',
> > +	'rendercopy_gen6.c',
> > +	'rendercopy_gen7.c',
> > +	'rendercopy_gen8.c',
> > +	'rendercopy_gen9.c',
> > +	'runnercomms.c',
> > +	'sw_sync.c',
> > +	'intel_aux_pgtable.c',
> > +	'intel_reg_map.c',
> > +	'intel_iosf.c',
> > +	'igt_kms.c',
> > +	'igt_fb.c',
> > +	'igt_core.c',
> > +	'igt_draw.c',
> > +	'igt_list.c',
> > +	'igt_map.c',
> > +	'igt_pm.c',
> > +	'igt_dummyload.c',
> > +	'igt_store.c',
> > +	'uwildmat/uwildmat.c',
> > +	'igt_kmod.c',
> > +	'igt_panfrost.c',
> > +	'igt_v3d.c',
> > +	'igt_vc4.c',
> > +	'igt_psr.c',
> > +	'igt_amd.c',
> > +	'igt_edid.c',
> > +	'igt_eld.c',
> > +	'igt_infoframe.c',
> > +	'veboxcopy_gen12.c',
> > +	'igt_msm.c',
> > +]
> > +
> > +lib_deps = [
> > +	cairo,
> > +	glib,
> > +	libatomic,
> > +	libdrm,
> > +	libdw,
> > +	libkmod,
> > +	libprocps,
> > +	libudev,
> > +	math,
> > +	pciaccess,
> > +	pixman,
> > +	pthreads,
> > +	realtime,
> > +	zlib
> > +]
> > +
> > +if libdrm_intel.found()
> > +	lib_deps += libdrm_intel
> > +else
> > +	lib_sources += 'stubs/drm/intel_bufmgr.c'
> > +	inc = [ inc, include_directories('stubs/drm') ]
> > +endif
> > +
> > +if libdrm_nouveau.found()
> > +	lib_deps += libdrm_nouveau
> > +	lib_sources += [
> > +		'igt_nouveau.c',
> > +		'nouveau/cea0b5.c'
> > +	]
> > +endif
> > +
> > +if libdrm_amdgpu.found()
> > +	lib_deps += libdrm_amdgpu
> > +	lib_sources += [
> > +		'amdgpu/amd_memory.c',
> > +		'amdgpu/amd_command_submission.c',
> > +		'amdgpu/amd_compute.c',
> > +		'amdgpu/amd_gfx.c',
> > +		'amdgpu/amd_ip_blocks.c',
> > +		'amdgpu/amd_shaders.c',
> > +		'amdgpu/amd_gfx_v8_0.c',
> > +		'amdgpu/amd_gfx_v9_0.c',
> > +		'amdgpu/amd_dispatch_helpers.c',
> > +		'amdgpu/amd_dispatch.c',
> > +		'amdgpu/amd_deadlock_helpers.c',
> > +		'amdgpu/amd_pci_unplug.c',
> > +		'amdgpu/xalloc.h'
> > +	]
> > +endif
> > +
> > +if libunwind.found()
> > +	lib_deps += libunwind
> > +else
> > +	inc = [ inc, include_directories('stubs/libunwind') ]
> > +endif
> > +
> > +if valgrind.found()
> > +	lib_deps += valgrind
> > +endif
> > +
> > +if gsl.found()
> > +	lib_deps += gsl
> > +	lib_sources += [ 'igt_frame.c', 'igt_audio.c' ]
> > +endif
> > +
> > +if alsa.found()
> > +	lib_deps += alsa
> > +	lib_sources += 'igt_alsa.c'
> > +endif
> > +
> > +if chamelium.found()
> > +	lib_deps += chamelium
> > +	lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
> > +	lib_sources += 'monitor_edids/monitor_edids_helper.c'
> > +endif
> > +
> > +if get_option('srcdir') != ''
> > +    srcdir = join_paths(get_option('srcdir'), 'tests')
> > +else
> > +    srcdir = join_paths(meson.source_root(), 'tests')
> > +endif
> > +
> > +if get_option('version_hash') != ''
> > +    vcs_command = ['echo', get_option('version_hash')]
> > +else
> > +    vcs_command = [ 'git', 'log', '-n1', '--pretty=format:g%h' ]
> > +endif
> > +
> > +lib_version = vcs_tag(input : 'version.h.in', output : 'version.h',
> > +		      fallback : 'NO-GIT',
> > +		      command : vcs_command )
> > +
> > +lib_intermediates = []
> > +foreach f: lib_sources
> > +    name = f.underscorify()
> > +    lib = static_library('igt-' + name,
> > +	[ f, lib_version ],
> > +	include_directories: inc,
> > +	dependencies : lib_deps,
> > +	c_args : [
> > +	    '-DIGT_DATADIR="@0@"'.format(join_paths(prefix, datadir)),
> > +	    '-DIGT_SRCDIR="@0@"'.format(srcdir),
> > +	    '-DIGT_LOG_DOMAIN="@0@"'.format(f.split('.')[0]),
> > +	])
> > +
> > +    lib_intermediates += lib
> > +endforeach
> > +
> > +lib_igt_build = shared_library('igt',
> > +    ['dummy.c'],
> > +    link_whole: lib_intermediates,
> > +    dependencies: lib_deps,
> > +    install : true,
> > +    soversion : '0',
> > +)
> > +
> > +lib_igt = declare_dependency(link_with : lib_igt_build,
> > +			    include_directories : inc)
> > +
> > +igt_deps = [ lib_igt ] + lib_deps
> > +
> > +lin_igt_chipset_build = static_library('igt_chipset',
> > +                                       ['intel_chipset.c',
> > +                                        'intel_device_info.c'],
> > +                                       include_directories : inc)
> > +
> > +lib_igt_chipset = declare_dependency(link_with : lin_igt_chipset_build,
> > +                                     include_directories : inc)
> > +
> > +lib_igt_perf_build = static_library('igt_perf',
> > +	['igt_perf.c'],
> > +	include_directories : inc)
> > +
> > +lib_igt_perf = declare_dependency(link_with : lib_igt_perf_build,
> > +				  include_directories : inc)
> > +
> > +scan_dep = [
> > +	glib,
> > +	libudev,
> > +]
> > +
> > +lib_igt_device_scan_build = static_library('igt_device_scan',
> > +	['igt_device_scan.c',
> > +	'igt_list.c',
> > +	'igt_tools_stub.c',
> > +	'intel_device_info.c',
> > +	],
> > +	dependencies : scan_dep,
> > +	include_directories : inc)
> > +
> > +lib_igt_device_scan = declare_dependency(link_with : lib_igt_device_scan_build,
> > +				  include_directories : inc)
> > +
> > +lib_igt_drm_fdinfo_build = static_library('igt_drm_fdinfo',
> > +	['igt_drm_fdinfo.c'],
> > +	include_directories : inc)
> > +
> > +lib_igt_drm_fdinfo = declare_dependency(link_with : lib_igt_drm_fdinfo_build,
> > +				  include_directories : inc)
> > +i915_perf_files = [
> > +  'igt_list.c',
> > +  'i915/perf.c',
> > +  'i915/perf_data_reader.c',
> > +]
> > +
> > +i915_perf_hardware = [
> > +  'hsw',
> > +  'bdw', 'chv',
> > +  'sklgt2', 'sklgt3', 'sklgt4',
> > +  'kblgt2', 'kblgt3',
> > +  'cflgt2', 'cflgt3',
> > +  'bxt', 'glk',
> > +  'cnl',
> > +  'icl', 'ehl',
> > +  'tglgt1', 'tglgt2', 'rkl', 'dg1', 'adl',
> > +  'acmgt1', 'acmgt2', 'acmgt3',
> > +]
> > +
> > +i915_xml_files = []
> > +foreach hw : i915_perf_hardware
> > +  i915_xml_files += files('i915/perf-configs/oa- at 0@.xml'.format(hw))
> > +endforeach
> > +
> > +i915_perf_files += custom_target(
> > +  'i915-perf-equations',
> > +  input : [ 'i915/perf-configs/perf-equations-codegen.py' ] + i915_xml_files,
> > +  output : [ 'i915_perf_equations.c', 'i915_perf_equations.h' ],
> > +  command : [
> > +    python3, '@INPUT0@',
> > +    '--code', '@OUTPUT0@',
> > +    '--header', '@OUTPUT1@',
> > +    i915_xml_files,
> > +  ])
> > +
> > +foreach hw : i915_perf_hardware
> > +  i915_perf_files += custom_target(
> > +    'i915-perf-registers- at 0@'.format(hw),
> > +    input : [ 'i915/perf-configs/perf-registers-codegen.py',
> > +              'i915/perf-configs/oa- at 0@.xml'.format(hw) ],
> > +    output : [ 'i915_perf_registers_ at 0@.c'.format(hw),
> > +               'i915_perf_registers_ at 0@.h'.format(hw), ],
> > +    command : [
> > +      python3, '@INPUT0@',
> > +      '--code', '@OUTPUT0@',
> > +      '--header', '@OUTPUT1@',
> > +      '--xml-file', '@INPUT1@'
> > +    ])
> > +  i915_perf_files += custom_target(
> > +    'i915-perf-metrics- at 0@'.format(hw),
> > +    input : [ 'i915/perf-configs/perf-metricset-codegen.py',
> > +              'i915/perf-configs/oa- at 0@.xml'.format(hw) ],
> > +    output : [ 'i915_perf_metrics_ at 0@.c'.format(hw),
> > +               'i915_perf_metrics_ at 0@.h'.format(hw), ],
> > +    command : [
> > +      python3, '@INPUT0@',
> > +      '--code', '@OUTPUT0@',
> > +      '--header', '@OUTPUT1@',
> > +      '--equations-include', 'i915_perf_equations.h',
> > +      '--registers-include', 'i915_perf_registers_ at 0@.h'.format(hw),
> > +      '--xml-file', '@INPUT1@',
> > +    ])
> > +endforeach
> > +
> > +lib_igt_i915_perf_build = shared_library(
> > +  'i915_perf',
> > +  i915_perf_files,
> > +  dependencies: lib_igt_chipset,
> > +  include_directories : inc,
> > +  install: true,
> > +  soversion: '1.5')
> > +
> > +lib_igt_i915_perf = declare_dependency(
> > +  link_with : lib_igt_i915_perf_build,
> > +  include_directories : inc)
> > +
> > +install_headers(
> > +  'igt_list.h',
> > +  'intel_chipset.h',
> > +  'i915/perf.h',
> > +  'i915/perf_data.h',
> > +  'i915/perf_data_reader.h',
> > +  subdir : 'i915-perf'
> > +)
> > +
> > +pkgconf = configuration_data()
> > +
> > +pkgconf.set('prefix', get_option('prefix'))
> > +pkgconf.set('exec_prefix', '${prefix}')
> > +pkgconf.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
> > +pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
> > +pkgconf.set('i915_perf_version', '1.5.1')
> > +
> > +configure_file(
> > +  input : 'i915-perf.pc.in',
> > +  output : 'i915-perf.pc',
> > +  configuration : pkgconf,
> > +  install_dir : pkgconfigdir)
> > +
> > +subdir('tests')
> > diff --git a/meson.build b/meson.build
> > index 3e937f5a..475680ac 100644
> > --- a/meson.build
> > +++ b/meson.build
> > @@ -121,7 +121,15 @@ build_info += 'With libdrm: ' + ','.join(libdrm_info)
> >  
> >  pciaccess = dependency('pciaccess', version : '>=0.10')
> >  libkmod = dependency('libkmod')
> > -libprocps = dependency('libprocps', required : true)
> > +libprocps = dependency('libprocps', required : false)
> > +libproc2 = dependency('libproc2', required : false)
> > +if libprocps.found()
> > +  config.set('HAVE_LIBPROCPS', 1)
> > +elif libproc2.found()
> > +  config.set('HAVE_LIBPROC2', 1)
> > +else
> > +  error('Either libprocps or libproc2 is required')
> > +endif
> >  
> >  libunwind = dependency('libunwind', required : get_option('libunwind'))
> >  build_info += 'With libunwind: @0@'.format(libunwind.found())
> > -- 
> > 2.30.2
> > 


More information about the igt-dev mailing list