aboutsummaryrefslogtreecommitdiff
path: root/tools/perf/util/target.c
blob: 051eaa68095e75a61b82aac4c479cf4c8101ef71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
 * Helper functions for handling target threads/cpus
 *
 * Copyright (C) 2012, LG Electronics, Namhyung Kim <namhyung.kim@lge.com>
 *
 * Released under the GPL v2.
 */

#include "target.h"
#include "debug.h"

#include <pwd.h>
#include <string.h>


enum perf_target_errno perf_target__validate(struct perf_target *target)
{
	enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS;

	if (target->pid)
		target->tid = target->pid;

	/* CPU and PID are mutually exclusive */
	if (target->tid && target->cpu_list) {
		target->cpu_list = NULL;
		if (ret == PERF_ERRNO_TARGET__SUCCESS)
			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU;
	}

	/* UID and PID are mutually exclusive */
	if (target->tid && target->uid_str) {
		target->uid_str = NULL;
		if (ret == PERF_ERRNO_TARGET__SUCCESS)
			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID;
	}

	/* UID and CPU are mutually exclusive */
	if (target->uid_str && target->cpu_list) {
		target->cpu_list = NULL;
		if (ret == PERF_ERRNO_TARGET__SUCCESS)
			ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU;
	}

	/* PID and SYSTEM are mutually exclusive */
	if (target->tid && target->system_wide) {
		target->system_wide = false;
		if (ret == PERF_ERRNO_TARGET__SUCCESS)
			ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM;
	}

	/* UID and SYSTEM are mutually exclusive */
	if (target->uid_str && target->system_wide) {
		target->system_wide = false;
		if (ret == PERF_ERRNO_TARGET__SUCCESS)
			ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM;
	}

	return ret;
}

enum perf_target_errno perf_target__parse_uid(struct perf_target *target)
{
	struct passwd pwd, *result;
	char buf[1024];
	const char *str = target->uid_str;

	target->uid = UINT_MAX;
	if (str == NULL)
		return PERF_ERRNO_TARGET__SUCCESS;

	/* Try user name first */
	getpwnam_r(str, &pwd, buf, sizeof(buf), &result);

	if (result == NULL) {
		/*
		 * The user name not found. Maybe it's a UID number.
		 */
		char *endptr;
		int uid = strtol(str, &endptr, 10);

		if (*endptr != '\0')
			return PERF_ERRNO_TARGET__INVALID_UID;

		getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);

		if (result == NULL)
			return PERF_ERRNO_TARGET__USER_NOT_FOUND;
	}

	target->uid = result->pw_uid;
	return PERF_ERRNO_TARGET__SUCCESS;
}

/*
 * This must have a same ordering as the enum perf_target_errno.
 */
static const char *perf_target__error_str[] = {
	"PID/TID switch overriding CPU",
	"PID/TID switch overriding UID",
	"UID switch overriding CPU",
	"PID/TID switch overriding SYSTEM",
	"UID switch overriding SYSTEM",
	"Invalid User: %s",
	"Problems obtaining information for user %s",
};

int perf_target__strerror(struct perf_target *target, int errnum,
			  char *buf, size_t buflen)
{
	int idx;
	const char *msg;

	BUG_ON(buflen == 0);

	if (errnum >= 0) {
		const char *err = strerror_r(errnum, buf, buflen);

		if (err != buf) {
			size_t len = strlen(err);
			char *c = mempcpy(buf, err, min(buflen - 1, len));
			*c = '\0';
		}

		return 0;
	}

	if (errnum <  __PERF_ERRNO_TARGET__START ||
	    errnum >= __PERF_ERRNO_TARGET__END)
		return -1;

	idx = errnum - __PERF_ERRNO_TARGET__START;
	msg = perf_target__error_str[idx];

	switch (errnum) {
	case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU
	 ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM:
		snprintf(buf, buflen, "%s", msg);
		break;

	case PERF_ERRNO_TARGET__INVALID_UID:
	case PERF_ERRNO_TARGET__USER_NOT_FOUND:
		snprintf(buf, buflen, msg, target->uid_str);
		break;

	default:
		/* cannot reach here */
		break;
	}

	return 0;
}