perf_counter tools: add in basic glue from Git
First very raw version at having a central 'perf' command and a list of subcommands: perf top perf stat perf record perf report ... This is done by picking up Git's collection of utility functions, and hacking them to build fine in this new environment. Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
d24e473e5b
commit
0780060124
35 changed files with 7953 additions and 859 deletions
179
Documentation/perf_counter/.gitignore
vendored
Normal file
179
Documentation/perf_counter/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
GIT-BUILD-OPTIONS
|
||||
GIT-CFLAGS
|
||||
GIT-GUI-VARS
|
||||
GIT-VERSION-FILE
|
||||
git
|
||||
git-add
|
||||
git-add--interactive
|
||||
git-am
|
||||
git-annotate
|
||||
git-apply
|
||||
git-archimport
|
||||
git-archive
|
||||
git-bisect
|
||||
git-bisect--helper
|
||||
git-blame
|
||||
git-branch
|
||||
git-bundle
|
||||
git-cat-file
|
||||
git-check-attr
|
||||
git-check-ref-format
|
||||
git-checkout
|
||||
git-checkout-index
|
||||
git-cherry
|
||||
git-cherry-pick
|
||||
git-clean
|
||||
git-clone
|
||||
git-commit
|
||||
git-commit-tree
|
||||
git-config
|
||||
git-count-objects
|
||||
git-cvsexportcommit
|
||||
git-cvsimport
|
||||
git-cvsserver
|
||||
git-daemon
|
||||
git-diff
|
||||
git-diff-files
|
||||
git-diff-index
|
||||
git-diff-tree
|
||||
git-difftool
|
||||
git-difftool--helper
|
||||
git-describe
|
||||
git-fast-export
|
||||
git-fast-import
|
||||
git-fetch
|
||||
git-fetch--tool
|
||||
git-fetch-pack
|
||||
git-filter-branch
|
||||
git-fmt-merge-msg
|
||||
git-for-each-ref
|
||||
git-format-patch
|
||||
git-fsck
|
||||
git-fsck-objects
|
||||
git-gc
|
||||
git-get-tar-commit-id
|
||||
git-grep
|
||||
git-hash-object
|
||||
git-help
|
||||
git-http-fetch
|
||||
git-http-push
|
||||
git-imap-send
|
||||
git-index-pack
|
||||
git-init
|
||||
git-init-db
|
||||
git-instaweb
|
||||
git-log
|
||||
git-lost-found
|
||||
git-ls-files
|
||||
git-ls-remote
|
||||
git-ls-tree
|
||||
git-mailinfo
|
||||
git-mailsplit
|
||||
git-merge
|
||||
git-merge-base
|
||||
git-merge-index
|
||||
git-merge-file
|
||||
git-merge-tree
|
||||
git-merge-octopus
|
||||
git-merge-one-file
|
||||
git-merge-ours
|
||||
git-merge-recursive
|
||||
git-merge-resolve
|
||||
git-merge-subtree
|
||||
git-mergetool
|
||||
git-mergetool--lib
|
||||
git-mktag
|
||||
git-mktree
|
||||
git-name-rev
|
||||
git-mv
|
||||
git-pack-redundant
|
||||
git-pack-objects
|
||||
git-pack-refs
|
||||
git-parse-remote
|
||||
git-patch-id
|
||||
git-peek-remote
|
||||
git-prune
|
||||
git-prune-packed
|
||||
git-pull
|
||||
git-push
|
||||
git-quiltimport
|
||||
git-read-tree
|
||||
git-rebase
|
||||
git-rebase--interactive
|
||||
git-receive-pack
|
||||
git-reflog
|
||||
git-relink
|
||||
git-remote
|
||||
git-repack
|
||||
git-repo-config
|
||||
git-request-pull
|
||||
git-rerere
|
||||
git-reset
|
||||
git-rev-list
|
||||
git-rev-parse
|
||||
git-revert
|
||||
git-rm
|
||||
git-send-email
|
||||
git-send-pack
|
||||
git-sh-setup
|
||||
git-shell
|
||||
git-shortlog
|
||||
git-show
|
||||
git-show-branch
|
||||
git-show-index
|
||||
git-show-ref
|
||||
git-stage
|
||||
git-stash
|
||||
git-status
|
||||
git-stripspace
|
||||
git-submodule
|
||||
git-svn
|
||||
git-symbolic-ref
|
||||
git-tag
|
||||
git-tar-tree
|
||||
git-unpack-file
|
||||
git-unpack-objects
|
||||
git-update-index
|
||||
git-update-ref
|
||||
git-update-server-info
|
||||
git-upload-archive
|
||||
git-upload-pack
|
||||
git-var
|
||||
git-verify-pack
|
||||
git-verify-tag
|
||||
git-web--browse
|
||||
git-whatchanged
|
||||
git-write-tree
|
||||
git-core-*/?*
|
||||
gitk-wish
|
||||
gitweb/gitweb.cgi
|
||||
test-chmtime
|
||||
test-ctype
|
||||
test-date
|
||||
test-delta
|
||||
test-dump-cache-tree
|
||||
test-genrandom
|
||||
test-match-trees
|
||||
test-parse-options
|
||||
test-path-utils
|
||||
test-sha1
|
||||
test-sigchain
|
||||
common-cmds.h
|
||||
*.tar.gz
|
||||
*.dsc
|
||||
*.deb
|
||||
git.spec
|
||||
*.exe
|
||||
*.[aos]
|
||||
*.py[co]
|
||||
config.mak
|
||||
autom4te.cache
|
||||
config.cache
|
||||
config.log
|
||||
config.status
|
||||
config.mak.autogen
|
||||
config.mak.append
|
||||
configure
|
||||
tags
|
||||
TAGS
|
||||
cscope*
|
||||
File diff suppressed because it is too large
Load diff
4
Documentation/perf_counter/PERF-BUILD-OPTIONS
Normal file
4
Documentation/perf_counter/PERF-BUILD-OPTIONS
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
SHELL_PATH='/bin/sh'
|
||||
TAR='tar'
|
||||
NO_CURL=''
|
||||
NO_PERL=''
|
||||
1
Documentation/perf_counter/PERF-CFLAGS
Normal file
1
Documentation/perf_counter/PERF-CFLAGS
Normal file
|
|
@ -0,0 +1 @@
|
|||
-g -O2 -Wall -DSHA1_HEADER='<openssl/sha.h>' : /home/mingo/bin:libexec/perf-core:share/perf-core/templates:/home/mingo
|
||||
1
Documentation/perf_counter/PERF-VERSION-FILE
Normal file
1
Documentation/perf_counter/PERF-VERSION-FILE
Normal file
|
|
@ -0,0 +1 @@
|
|||
PERF_VERSION = 0.0.1.PERF
|
||||
42
Documentation/perf_counter/PERF-VERSION-GEN
Executable file
42
Documentation/perf_counter/PERF-VERSION-GEN
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
|
||||
GVF=PERF-VERSION-FILE
|
||||
DEF_VER=v0.0.1.PERF
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
# First see if there is a version file (included in release tarballs),
|
||||
# then try git-describe, then default.
|
||||
if test -f version
|
||||
then
|
||||
VN=$(cat version) || VN="$DEF_VER"
|
||||
elif test -d .git -o -f .git &&
|
||||
VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
|
||||
case "$VN" in
|
||||
*$LF*) (exit 1) ;;
|
||||
v[0-9]*)
|
||||
git update-index -q --refresh
|
||||
test -z "$(git diff-index --name-only HEAD --)" ||
|
||||
VN="$VN-dirty" ;;
|
||||
esac
|
||||
then
|
||||
VN=$(echo "$VN" | sed -e 's/-/./g');
|
||||
else
|
||||
VN="$DEF_VER"
|
||||
fi
|
||||
|
||||
VN=$(expr "$VN" : v*'\(.*\)')
|
||||
|
||||
if test -r $GVF
|
||||
then
|
||||
VC=$(sed -e 's/^PERF_VERSION = //' <$GVF)
|
||||
else
|
||||
VC=unset
|
||||
fi
|
||||
test "$VN" = "$VC" || {
|
||||
echo >&2 "PERF_VERSION = $VN"
|
||||
echo "PERF_VERSION = $VN" >$GVF
|
||||
}
|
||||
|
||||
|
||||
117
Documentation/perf_counter/abspath.c
Normal file
117
Documentation/perf_counter/abspath.c
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#include "cache.h"
|
||||
|
||||
/*
|
||||
* Do not use this for inspecting *tracked* content. When path is a
|
||||
* symlink to a directory, we do not want to say it is a directory when
|
||||
* dealing with tracked content in the working tree.
|
||||
*/
|
||||
int is_directory(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
return (!stat(path, &st) && S_ISDIR(st.st_mode));
|
||||
}
|
||||
|
||||
/* We allow "recursive" symbolic links. Only within reason, though. */
|
||||
#define MAXDEPTH 5
|
||||
|
||||
const char *make_absolute_path(const char *path)
|
||||
{
|
||||
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
|
||||
char cwd[1024] = "";
|
||||
int buf_index = 1, len;
|
||||
|
||||
int depth = MAXDEPTH;
|
||||
char *last_elem = NULL;
|
||||
struct stat st;
|
||||
|
||||
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
|
||||
die ("Too long path: %.*s", 60, path);
|
||||
|
||||
while (depth--) {
|
||||
if (!is_directory(buf)) {
|
||||
char *last_slash = strrchr(buf, '/');
|
||||
if (last_slash) {
|
||||
*last_slash = '\0';
|
||||
last_elem = xstrdup(last_slash + 1);
|
||||
} else {
|
||||
last_elem = xstrdup(buf);
|
||||
*buf = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (*buf) {
|
||||
if (!*cwd && !getcwd(cwd, sizeof(cwd)))
|
||||
die ("Could not get current working directory");
|
||||
|
||||
if (chdir(buf))
|
||||
die ("Could not switch to '%s'", buf);
|
||||
}
|
||||
if (!getcwd(buf, PATH_MAX))
|
||||
die ("Could not get current working directory");
|
||||
|
||||
if (last_elem) {
|
||||
int len = strlen(buf);
|
||||
if (len + strlen(last_elem) + 2 > PATH_MAX)
|
||||
die ("Too long path name: '%s/%s'",
|
||||
buf, last_elem);
|
||||
buf[len] = '/';
|
||||
strcpy(buf + len + 1, last_elem);
|
||||
free(last_elem);
|
||||
last_elem = NULL;
|
||||
}
|
||||
|
||||
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
|
||||
len = readlink(buf, next_buf, PATH_MAX);
|
||||
if (len < 0)
|
||||
die ("Invalid symlink: %s", buf);
|
||||
if (PATH_MAX <= len)
|
||||
die("symbolic link too long: %s", buf);
|
||||
next_buf[len] = '\0';
|
||||
buf = next_buf;
|
||||
buf_index = 1 - buf_index;
|
||||
next_buf = bufs[buf_index];
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (*cwd && chdir(cwd))
|
||||
die ("Could not change back to '%s'", cwd);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *get_pwd_cwd(void)
|
||||
{
|
||||
static char cwd[PATH_MAX + 1];
|
||||
char *pwd;
|
||||
struct stat cwd_stat, pwd_stat;
|
||||
if (getcwd(cwd, PATH_MAX) == NULL)
|
||||
return NULL;
|
||||
pwd = getenv("PWD");
|
||||
if (pwd && strcmp(pwd, cwd)) {
|
||||
stat(cwd, &cwd_stat);
|
||||
if (!stat(pwd, &pwd_stat) &&
|
||||
pwd_stat.st_dev == cwd_stat.st_dev &&
|
||||
pwd_stat.st_ino == cwd_stat.st_ino) {
|
||||
strlcpy(cwd, pwd, PATH_MAX);
|
||||
}
|
||||
}
|
||||
return cwd;
|
||||
}
|
||||
|
||||
const char *make_nonrelative_path(const char *path)
|
||||
{
|
||||
static char buf[PATH_MAX + 1];
|
||||
|
||||
if (is_absolute_path(path)) {
|
||||
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
|
||||
die("Too long path: %.*s", 60, path);
|
||||
} else {
|
||||
const char *cwd = get_pwd_cwd();
|
||||
if (!cwd)
|
||||
die("Cannot determine the current working directory");
|
||||
if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
|
||||
die("Too long path: %.*s", 60, path);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
77
Documentation/perf_counter/alias.c
Normal file
77
Documentation/perf_counter/alias.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#include "cache.h"
|
||||
|
||||
static const char *alias_key;
|
||||
static char *alias_val;
|
||||
|
||||
static int alias_lookup_cb(const char *k, const char *v, void *cb)
|
||||
{
|
||||
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
|
||||
if (!v)
|
||||
return config_error_nonbool(k);
|
||||
alias_val = strdup(v);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *alias_lookup(const char *alias)
|
||||
{
|
||||
alias_key = alias;
|
||||
alias_val = NULL;
|
||||
perf_config(alias_lookup_cb, NULL);
|
||||
return alias_val;
|
||||
}
|
||||
|
||||
int split_cmdline(char *cmdline, const char ***argv)
|
||||
{
|
||||
int src, dst, count = 0, size = 16;
|
||||
char quoted = 0;
|
||||
|
||||
*argv = malloc(sizeof(char*) * size);
|
||||
|
||||
/* split alias_string */
|
||||
(*argv)[count++] = cmdline;
|
||||
for (src = dst = 0; cmdline[src];) {
|
||||
char c = cmdline[src];
|
||||
if (!quoted && isspace(c)) {
|
||||
cmdline[dst++] = 0;
|
||||
while (cmdline[++src]
|
||||
&& isspace(cmdline[src]))
|
||||
; /* skip */
|
||||
if (count >= size) {
|
||||
size += 16;
|
||||
*argv = realloc(*argv, sizeof(char*) * size);
|
||||
}
|
||||
(*argv)[count++] = cmdline + dst;
|
||||
} else if (!quoted && (c == '\'' || c == '"')) {
|
||||
quoted = c;
|
||||
src++;
|
||||
} else if (c == quoted) {
|
||||
quoted = 0;
|
||||
src++;
|
||||
} else {
|
||||
if (c == '\\' && quoted != '\'') {
|
||||
src++;
|
||||
c = cmdline[src];
|
||||
if (!c) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("cmdline ends with \\");
|
||||
}
|
||||
}
|
||||
cmdline[dst++] = c;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
cmdline[dst] = 0;
|
||||
|
||||
if (quoted) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("unclosed quote");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
463
Documentation/perf_counter/builtin-help.c
Normal file
463
Documentation/perf_counter/builtin-help.c
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* builtin-help.c
|
||||
*
|
||||
* Builtin help command
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "common-cmds.h"
|
||||
#include "parse-options.h"
|
||||
#include "run-command.h"
|
||||
#include "help.h"
|
||||
|
||||
static struct man_viewer_list {
|
||||
struct man_viewer_list *next;
|
||||
char name[FLEX_ARRAY];
|
||||
} *man_viewer_list;
|
||||
|
||||
static struct man_viewer_info_list {
|
||||
struct man_viewer_info_list *next;
|
||||
const char *info;
|
||||
char name[FLEX_ARRAY];
|
||||
} *man_viewer_info_list;
|
||||
|
||||
enum help_format {
|
||||
HELP_FORMAT_MAN,
|
||||
HELP_FORMAT_INFO,
|
||||
HELP_FORMAT_WEB,
|
||||
};
|
||||
|
||||
static int show_all = 0;
|
||||
static enum help_format help_format = HELP_FORMAT_MAN;
|
||||
static struct option builtin_help_options[] = {
|
||||
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
|
||||
OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
|
||||
OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
|
||||
HELP_FORMAT_WEB),
|
||||
OPT_SET_INT('i', "info", &help_format, "show info page",
|
||||
HELP_FORMAT_INFO),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
static const char * const builtin_help_usage[] = {
|
||||
"perf help [--all] [--man|--web|--info] [command]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static enum help_format parse_help_format(const char *format)
|
||||
{
|
||||
if (!strcmp(format, "man"))
|
||||
return HELP_FORMAT_MAN;
|
||||
if (!strcmp(format, "info"))
|
||||
return HELP_FORMAT_INFO;
|
||||
if (!strcmp(format, "web") || !strcmp(format, "html"))
|
||||
return HELP_FORMAT_WEB;
|
||||
die("unrecognized help format '%s'", format);
|
||||
}
|
||||
|
||||
static const char *get_man_viewer_info(const char *name)
|
||||
{
|
||||
struct man_viewer_info_list *viewer;
|
||||
|
||||
for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
|
||||
{
|
||||
if (!strcasecmp(name, viewer->name))
|
||||
return viewer->info;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int check_emacsclient_version(void)
|
||||
{
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
struct child_process ec_process;
|
||||
const char *argv_ec[] = { "emacsclient", "--version", NULL };
|
||||
int version;
|
||||
|
||||
/* emacsclient prints its version number on stderr */
|
||||
memset(&ec_process, 0, sizeof(ec_process));
|
||||
ec_process.argv = argv_ec;
|
||||
ec_process.err = -1;
|
||||
ec_process.stdout_to_stderr = 1;
|
||||
if (start_command(&ec_process)) {
|
||||
fprintf(stderr, "Failed to start emacsclient.\n");
|
||||
return -1;
|
||||
}
|
||||
strbuf_read(&buffer, ec_process.err, 20);
|
||||
close(ec_process.err);
|
||||
|
||||
/*
|
||||
* Don't bother checking return value, because "emacsclient --version"
|
||||
* seems to always exits with code 1.
|
||||
*/
|
||||
finish_command(&ec_process);
|
||||
|
||||
if (prefixcmp(buffer.buf, "emacsclient")) {
|
||||
fprintf(stderr, "Failed to parse emacsclient version.\n");
|
||||
strbuf_release(&buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_remove(&buffer, 0, strlen("emacsclient"));
|
||||
version = atoi(buffer.buf);
|
||||
|
||||
if (version < 22) {
|
||||
fprintf(stderr,
|
||||
"emacsclient version '%d' too old (< 22).\n",
|
||||
version);
|
||||
strbuf_release(&buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_release(&buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exec_woman_emacs(const char* path, const char *page)
|
||||
{
|
||||
if (!check_emacsclient_version()) {
|
||||
/* This works only with emacsclient version >= 22. */
|
||||
struct strbuf man_page = STRBUF_INIT;
|
||||
|
||||
if (!path)
|
||||
path = "emacsclient";
|
||||
strbuf_addf(&man_page, "(woman \"%s\")", page);
|
||||
execlp(path, "emacsclient", "-e", man_page.buf, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void exec_man_konqueror(const char* path, const char *page)
|
||||
{
|
||||
const char *display = getenv("DISPLAY");
|
||||
if (display && *display) {
|
||||
struct strbuf man_page = STRBUF_INIT;
|
||||
const char *filename = "kfmclient";
|
||||
|
||||
/* It's simpler to launch konqueror using kfmclient. */
|
||||
if (path) {
|
||||
const char *file = strrchr(path, '/');
|
||||
if (file && !strcmp(file + 1, "konqueror")) {
|
||||
char *new = strdup(path);
|
||||
char *dest = strrchr(new, '/');
|
||||
|
||||
/* strlen("konqueror") == strlen("kfmclient") */
|
||||
strcpy(dest + 1, "kfmclient");
|
||||
path = new;
|
||||
}
|
||||
if (file)
|
||||
filename = file;
|
||||
} else
|
||||
path = "kfmclient";
|
||||
strbuf_addf(&man_page, "man:%s(1)", page);
|
||||
execlp(path, filename, "newTab", man_page.buf, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void exec_man_man(const char* path, const char *page)
|
||||
{
|
||||
if (!path)
|
||||
path = "man";
|
||||
execlp(path, "man", page, NULL);
|
||||
warning("failed to exec '%s': %s", path, strerror(errno));
|
||||
}
|
||||
|
||||
static void exec_man_cmd(const char *cmd, const char *page)
|
||||
{
|
||||
struct strbuf shell_cmd = STRBUF_INIT;
|
||||
strbuf_addf(&shell_cmd, "%s %s", cmd, page);
|
||||
execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
|
||||
warning("failed to exec '%s': %s", cmd, strerror(errno));
|
||||
}
|
||||
|
||||
static void add_man_viewer(const char *name)
|
||||
{
|
||||
struct man_viewer_list **p = &man_viewer_list;
|
||||
size_t len = strlen(name);
|
||||
|
||||
while (*p)
|
||||
p = &((*p)->next);
|
||||
*p = calloc(1, (sizeof(**p) + len + 1));
|
||||
strncpy((*p)->name, name, len);
|
||||
}
|
||||
|
||||
static int supported_man_viewer(const char *name, size_t len)
|
||||
{
|
||||
return (!strncasecmp("man", name, len) ||
|
||||
!strncasecmp("woman", name, len) ||
|
||||
!strncasecmp("konqueror", name, len));
|
||||
}
|
||||
|
||||
static void do_add_man_viewer_info(const char *name,
|
||||
size_t len,
|
||||
const char *value)
|
||||
{
|
||||
struct man_viewer_info_list *new = calloc(1, sizeof(*new) + len + 1);
|
||||
|
||||
strncpy(new->name, name, len);
|
||||
new->info = strdup(value);
|
||||
new->next = man_viewer_info_list;
|
||||
man_viewer_info_list = new;
|
||||
}
|
||||
|
||||
static int add_man_viewer_path(const char *name,
|
||||
size_t len,
|
||||
const char *value)
|
||||
{
|
||||
if (supported_man_viewer(name, len))
|
||||
do_add_man_viewer_info(name, len, value);
|
||||
else
|
||||
warning("'%s': path for unsupported man viewer.\n"
|
||||
"Please consider using 'man.<tool>.cmd' instead.",
|
||||
name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_man_viewer_cmd(const char *name,
|
||||
size_t len,
|
||||
const char *value)
|
||||
{
|
||||
if (supported_man_viewer(name, len))
|
||||
warning("'%s': cmd for supported man viewer.\n"
|
||||
"Please consider using 'man.<tool>.path' instead.",
|
||||
name);
|
||||
else
|
||||
do_add_man_viewer_info(name, len, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_man_viewer_info(const char *var, const char *value)
|
||||
{
|
||||
const char *name = var + 4;
|
||||
const char *subkey = strrchr(name, '.');
|
||||
|
||||
if (!subkey)
|
||||
return error("Config with no key for man viewer: %s", name);
|
||||
|
||||
if (!strcmp(subkey, ".path")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
return add_man_viewer_path(name, subkey - name, value);
|
||||
}
|
||||
if (!strcmp(subkey, ".cmd")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
return add_man_viewer_cmd(name, subkey - name, value);
|
||||
}
|
||||
|
||||
warning("'%s': unsupported man viewer sub key.", subkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_help_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "help.format")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
help_format = parse_help_format(value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "man.viewer")) {
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
add_man_viewer(value);
|
||||
return 0;
|
||||
}
|
||||
if (!prefixcmp(var, "man."))
|
||||
return add_man_viewer_info(var, value);
|
||||
|
||||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static struct cmdnames main_cmds, other_cmds;
|
||||
|
||||
void list_common_cmds_help(void)
|
||||
{
|
||||
int i, longest = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
if (longest < strlen(common_cmds[i].name))
|
||||
longest = strlen(common_cmds[i].name);
|
||||
}
|
||||
|
||||
puts("The most commonly used perf commands are:");
|
||||
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
|
||||
printf(" %s ", common_cmds[i].name);
|
||||
mput_char(' ', longest - strlen(common_cmds[i].name));
|
||||
puts(common_cmds[i].help);
|
||||
}
|
||||
}
|
||||
|
||||
static int is_perf_command(const char *s)
|
||||
{
|
||||
return is_in_cmdlist(&main_cmds, s) ||
|
||||
is_in_cmdlist(&other_cmds, s);
|
||||
}
|
||||
|
||||
static const char *prepend(const char *prefix, const char *cmd)
|
||||
{
|
||||
size_t pre_len = strlen(prefix);
|
||||
size_t cmd_len = strlen(cmd);
|
||||
char *p = malloc(pre_len + cmd_len + 1);
|
||||
memcpy(p, prefix, pre_len);
|
||||
strcpy(p + pre_len, cmd);
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char *cmd_to_page(const char *perf_cmd)
|
||||
{
|
||||
if (!perf_cmd)
|
||||
return "perf";
|
||||
else if (!prefixcmp(perf_cmd, "perf"))
|
||||
return perf_cmd;
|
||||
else if (is_perf_command(perf_cmd))
|
||||
return prepend("perf-", perf_cmd);
|
||||
else
|
||||
return prepend("perf", perf_cmd);
|
||||
}
|
||||
|
||||
static void setup_man_path(void)
|
||||
{
|
||||
struct strbuf new_path = STRBUF_INIT;
|
||||
const char *old_path = getenv("MANPATH");
|
||||
|
||||
/* We should always put ':' after our path. If there is no
|
||||
* old_path, the ':' at the end will let 'man' to try
|
||||
* system-wide paths after ours to find the manual page. If
|
||||
* there is old_path, we need ':' as delimiter. */
|
||||
strbuf_addstr(&new_path, system_path(PERF_MAN_PATH));
|
||||
strbuf_addch(&new_path, ':');
|
||||
if (old_path)
|
||||
strbuf_addstr(&new_path, old_path);
|
||||
|
||||
setenv("MANPATH", new_path.buf, 1);
|
||||
|
||||
strbuf_release(&new_path);
|
||||
}
|
||||
|
||||
static void exec_viewer(const char *name, const char *page)
|
||||
{
|
||||
const char *info = get_man_viewer_info(name);
|
||||
|
||||
if (!strcasecmp(name, "man"))
|
||||
exec_man_man(info, page);
|
||||
else if (!strcasecmp(name, "woman"))
|
||||
exec_woman_emacs(info, page);
|
||||
else if (!strcasecmp(name, "konqueror"))
|
||||
exec_man_konqueror(info, page);
|
||||
else if (info)
|
||||
exec_man_cmd(info, page);
|
||||
else
|
||||
warning("'%s': unknown man viewer.", name);
|
||||
}
|
||||
|
||||
static void show_man_page(const char *perf_cmd)
|
||||
{
|
||||
struct man_viewer_list *viewer;
|
||||
const char *page = cmd_to_page(perf_cmd);
|
||||
const char *fallback = getenv("PERF_MAN_VIEWER");
|
||||
|
||||
setup_man_path();
|
||||
for (viewer = man_viewer_list; viewer; viewer = viewer->next)
|
||||
{
|
||||
exec_viewer(viewer->name, page); /* will return when unable */
|
||||
}
|
||||
if (fallback)
|
||||
exec_viewer(fallback, page);
|
||||
exec_viewer("man", page);
|
||||
die("no man viewer handled the request");
|
||||
}
|
||||
|
||||
static void show_info_page(const char *perf_cmd)
|
||||
{
|
||||
const char *page = cmd_to_page(perf_cmd);
|
||||
setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
|
||||
execlp("info", "info", "perfman", page, NULL);
|
||||
}
|
||||
|
||||
static void get_html_page_path(struct strbuf *page_path, const char *page)
|
||||
{
|
||||
struct stat st;
|
||||
const char *html_path = system_path(PERF_HTML_PATH);
|
||||
|
||||
/* Check that we have a perf documentation directory. */
|
||||
if (stat(mkpath("%s/perf.html", html_path), &st)
|
||||
|| !S_ISREG(st.st_mode))
|
||||
die("'%s': not a documentation directory.", html_path);
|
||||
|
||||
strbuf_init(page_path, 0);
|
||||
strbuf_addf(page_path, "%s/%s.html", html_path, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* If open_html is not defined in a platform-specific way (see for
|
||||
* example compat/mingw.h), we use the script web--browse to display
|
||||
* HTML.
|
||||
*/
|
||||
#ifndef open_html
|
||||
void open_html(const char *path)
|
||||
{
|
||||
execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void show_html_page(const char *perf_cmd)
|
||||
{
|
||||
const char *page = cmd_to_page(perf_cmd);
|
||||
struct strbuf page_path; /* it leaks but we exec bellow */
|
||||
|
||||
get_html_page_path(&page_path, page);
|
||||
|
||||
open_html(page_path.buf);
|
||||
}
|
||||
|
||||
int cmd_help(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int nonperf;
|
||||
const char *alias;
|
||||
load_command_list("perf-", &main_cmds, &other_cmds);
|
||||
|
||||
/* setup_perf_directory_gently(&nonperf); */
|
||||
perf_config(perf_help_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, builtin_help_options,
|
||||
builtin_help_usage, 0);
|
||||
|
||||
if (show_all) {
|
||||
printf("usage: %s\n\n", perf_usage_string);
|
||||
list_commands("perf commands", &main_cmds, &other_cmds);
|
||||
printf("%s\n", perf_more_info_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!argv[0]) {
|
||||
printf("usage: %s\n\n", perf_usage_string);
|
||||
list_common_cmds_help();
|
||||
printf("\n%s\n", perf_more_info_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
alias = alias_lookup(argv[0]);
|
||||
if (alias && !is_perf_command(argv[0])) {
|
||||
printf("`perf %s' is aliased to `%s'\n", argv[0], alias);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (help_format) {
|
||||
case HELP_FORMAT_MAN:
|
||||
show_man_page(argv[0]);
|
||||
break;
|
||||
case HELP_FORMAT_INFO:
|
||||
show_info_page(argv[0]);
|
||||
break;
|
||||
case HELP_FORMAT_WEB:
|
||||
show_html_page(argv[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
1411
Documentation/perf_counter/builtin-top.c
Normal file
1411
Documentation/perf_counter/builtin-top.c
Normal file
File diff suppressed because it is too large
Load diff
18
Documentation/perf_counter/builtin.h
Normal file
18
Documentation/perf_counter/builtin.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef BUILTIN_H
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
extern const char perf_version_string[];
|
||||
extern const char perf_usage_string[];
|
||||
extern const char perf_more_info_string[];
|
||||
|
||||
extern void list_common_cmds_help(void);
|
||||
extern const char *help_unknown_cmd(const char *cmd);
|
||||
extern void prune_packed_objects(int);
|
||||
extern int read_line_with_nul(char *buf, int size, FILE *file);
|
||||
extern int check_pager_config(const char *cmd);
|
||||
|
||||
extern int cmd_top(int argc, const char **argv, const char *prefix);
|
||||
#endif
|
||||
97
Documentation/perf_counter/cache.h
Normal file
97
Documentation/perf_counter/cache.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef CACHE_H
|
||||
#define CACHE_H
|
||||
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
#define PERF_DIR_ENVIRONMENT "PERF_DIR"
|
||||
#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
|
||||
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
|
||||
#define DB_ENVIRONMENT "PERF_OBJECT_DIRECTORY"
|
||||
#define INDEX_ENVIRONMENT "PERF_INDEX_FILE"
|
||||
#define GRAFT_ENVIRONMENT "PERF_GRAFT_FILE"
|
||||
#define TEMPLATE_DIR_ENVIRONMENT "PERF_TEMPLATE_DIR"
|
||||
#define CONFIG_ENVIRONMENT "PERF_CONFIG"
|
||||
#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
|
||||
#define CEILING_DIRECTORIES_ENVIRONMENT "PERF_CEILING_DIRECTORIES"
|
||||
#define PERFATTRIBUTES_FILE ".perfattributes"
|
||||
#define INFOATTRIBUTES_FILE "info/attributes"
|
||||
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
|
||||
|
||||
typedef int (*config_fn_t)(const char *, const char *, void *);
|
||||
extern int perf_default_config(const char *, const char *, void *);
|
||||
extern int perf_config_from_file(config_fn_t fn, const char *, void *);
|
||||
extern int perf_config(config_fn_t fn, void *);
|
||||
extern int perf_parse_ulong(const char *, unsigned long *);
|
||||
extern int perf_config_int(const char *, const char *);
|
||||
extern unsigned long perf_config_ulong(const char *, const char *);
|
||||
extern int perf_config_bool_or_int(const char *, const char *, int *);
|
||||
extern int perf_config_bool(const char *, const char *);
|
||||
extern int perf_config_string(const char **, const char *, const char *);
|
||||
extern int perf_config_set(const char *, const char *);
|
||||
extern int perf_config_set_multivar(const char *, const char *, const char *, int);
|
||||
extern int perf_config_rename_section(const char *, const char *);
|
||||
extern const char *perf_etc_perfconfig(void);
|
||||
extern int check_repository_format_version(const char *var, const char *value, void *cb);
|
||||
extern int perf_config_system(void);
|
||||
extern int perf_config_global(void);
|
||||
extern int config_error_nonbool(const char *);
|
||||
extern const char *config_exclusive_filename;
|
||||
|
||||
#define MAX_PERFNAME (1000)
|
||||
extern char perf_default_email[MAX_PERFNAME];
|
||||
extern char perf_default_name[MAX_PERFNAME];
|
||||
extern int user_ident_explicitly_given;
|
||||
|
||||
extern const char *perf_log_output_encoding;
|
||||
extern const char *perf_mailmap_file;
|
||||
|
||||
/* IO helper functions */
|
||||
extern void maybe_flush_or_die(FILE *, const char *);
|
||||
extern int copy_fd(int ifd, int ofd);
|
||||
extern int copy_file(const char *dst, const char *src, int mode);
|
||||
extern ssize_t read_in_full(int fd, void *buf, size_t count);
|
||||
extern ssize_t write_in_full(int fd, const void *buf, size_t count);
|
||||
extern void write_or_die(int fd, const void *buf, size_t count);
|
||||
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
|
||||
extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
|
||||
extern void fsync_or_die(int fd, const char *);
|
||||
|
||||
/* pager.c */
|
||||
extern void setup_pager(void);
|
||||
extern const char *pager_program;
|
||||
extern int pager_in_use(void);
|
||||
extern int pager_use_color;
|
||||
|
||||
extern const char *editor_program;
|
||||
extern const char *excludes_file;
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
int split_cmdline(char *cmdline, const char ***argv);
|
||||
|
||||
#define alloc_nr(x) (((x)+16)*3/2)
|
||||
|
||||
/*
|
||||
* Realloc the buffer pointed at by variable 'x' so that it can hold
|
||||
* at least 'nr' entries; the number of entries currently allocated
|
||||
* is 'alloc', using the standard growing factor alloc_nr() macro.
|
||||
*
|
||||
* DO NOT USE any expression with side-effect for 'x' or 'alloc'.
|
||||
*/
|
||||
#define ALLOC_GROW(x, nr, alloc) \
|
||||
do { \
|
||||
if ((nr) > alloc) { \
|
||||
if (alloc_nr(alloc) < (nr)) \
|
||||
alloc = (nr); \
|
||||
else \
|
||||
alloc = alloc_nr(alloc); \
|
||||
x = xrealloc((x), alloc * sizeof(*(x))); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
static inline int is_absolute_path(const char *path)
|
||||
{
|
||||
return path[0] == '/';
|
||||
}
|
||||
#endif /* CACHE_H */
|
||||
4
Documentation/perf_counter/command-list.txt
Normal file
4
Documentation/perf_counter/command-list.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# List of known perf commands.
|
||||
# command name category [deprecated] [common]
|
||||
perf-top mainporcelain common
|
||||
|
||||
966
Documentation/perf_counter/config.c
Normal file
966
Documentation/perf_counter/config.c
Normal file
|
|
@ -0,0 +1,966 @@
|
|||
/*
|
||||
* GIT - The information manager from hell
|
||||
*
|
||||
* Copyright (C) Linus Torvalds, 2005
|
||||
* Copyright (C) Johannes Schindelin, 2005
|
||||
*
|
||||
*/
|
||||
#include "util.h"
|
||||
#include "cache.h"
|
||||
#include "exec_cmd.h"
|
||||
|
||||
#define MAXNAME (256)
|
||||
|
||||
static FILE *config_file;
|
||||
static const char *config_file_name;
|
||||
static int config_linenr;
|
||||
static int config_file_eof;
|
||||
static int zlib_compression_seen;
|
||||
|
||||
const char *config_exclusive_filename = NULL;
|
||||
|
||||
static int get_next_char(void)
|
||||
{
|
||||
int c;
|
||||
FILE *f;
|
||||
|
||||
c = '\n';
|
||||
if ((f = config_file) != NULL) {
|
||||
c = fgetc(f);
|
||||
if (c == '\r') {
|
||||
/* DOS like systems */
|
||||
c = fgetc(f);
|
||||
if (c != '\n') {
|
||||
ungetc(c, f);
|
||||
c = '\r';
|
||||
}
|
||||
}
|
||||
if (c == '\n')
|
||||
config_linenr++;
|
||||
if (c == EOF) {
|
||||
config_file_eof = 1;
|
||||
c = '\n';
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static char *parse_value(void)
|
||||
{
|
||||
static char value[1024];
|
||||
int quote = 0, comment = 0, len = 0, space = 0;
|
||||
|
||||
for (;;) {
|
||||
int c = get_next_char();
|
||||
if (len >= sizeof(value) - 1)
|
||||
return NULL;
|
||||
if (c == '\n') {
|
||||
if (quote)
|
||||
return NULL;
|
||||
value[len] = 0;
|
||||
return value;
|
||||
}
|
||||
if (comment)
|
||||
continue;
|
||||
if (isspace(c) && !quote) {
|
||||
space = 1;
|
||||
continue;
|
||||
}
|
||||
if (!quote) {
|
||||
if (c == ';' || c == '#') {
|
||||
comment = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (space) {
|
||||
if (len)
|
||||
value[len++] = ' ';
|
||||
space = 0;
|
||||
}
|
||||
if (c == '\\') {
|
||||
c = get_next_char();
|
||||
switch (c) {
|
||||
case '\n':
|
||||
continue;
|
||||
case 't':
|
||||
c = '\t';
|
||||
break;
|
||||
case 'b':
|
||||
c = '\b';
|
||||
break;
|
||||
case 'n':
|
||||
c = '\n';
|
||||
break;
|
||||
/* Some characters escape as themselves */
|
||||
case '\\': case '"':
|
||||
break;
|
||||
/* Reject unknown escape sequences */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
value[len++] = c;
|
||||
continue;
|
||||
}
|
||||
if (c == '"') {
|
||||
quote = 1-quote;
|
||||
continue;
|
||||
}
|
||||
value[len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int iskeychar(int c)
|
||||
{
|
||||
return isalnum(c) || c == '-';
|
||||
}
|
||||
|
||||
static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
|
||||
{
|
||||
int c;
|
||||
char *value;
|
||||
|
||||
/* Get the full name */
|
||||
for (;;) {
|
||||
c = get_next_char();
|
||||
if (config_file_eof)
|
||||
break;
|
||||
if (!iskeychar(c))
|
||||
break;
|
||||
name[len++] = tolower(c);
|
||||
if (len >= MAXNAME)
|
||||
return -1;
|
||||
}
|
||||
name[len] = 0;
|
||||
while (c == ' ' || c == '\t')
|
||||
c = get_next_char();
|
||||
|
||||
value = NULL;
|
||||
if (c != '\n') {
|
||||
if (c != '=')
|
||||
return -1;
|
||||
value = parse_value();
|
||||
if (!value)
|
||||
return -1;
|
||||
}
|
||||
return fn(name, value, data);
|
||||
}
|
||||
|
||||
static int get_extended_base_var(char *name, int baselen, int c)
|
||||
{
|
||||
do {
|
||||
if (c == '\n')
|
||||
return -1;
|
||||
c = get_next_char();
|
||||
} while (isspace(c));
|
||||
|
||||
/* We require the format to be '[base "extension"]' */
|
||||
if (c != '"')
|
||||
return -1;
|
||||
name[baselen++] = '.';
|
||||
|
||||
for (;;) {
|
||||
int c = get_next_char();
|
||||
if (c == '\n')
|
||||
return -1;
|
||||
if (c == '"')
|
||||
break;
|
||||
if (c == '\\') {
|
||||
c = get_next_char();
|
||||
if (c == '\n')
|
||||
return -1;
|
||||
}
|
||||
name[baselen++] = c;
|
||||
if (baselen > MAXNAME / 2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Final ']' */
|
||||
if (get_next_char() != ']')
|
||||
return -1;
|
||||
return baselen;
|
||||
}
|
||||
|
||||
static int get_base_var(char *name)
|
||||
{
|
||||
int baselen = 0;
|
||||
|
||||
for (;;) {
|
||||
int c = get_next_char();
|
||||
if (config_file_eof)
|
||||
return -1;
|
||||
if (c == ']')
|
||||
return baselen;
|
||||
if (isspace(c))
|
||||
return get_extended_base_var(name, baselen, c);
|
||||
if (!iskeychar(c) && c != '.')
|
||||
return -1;
|
||||
if (baselen > MAXNAME / 2)
|
||||
return -1;
|
||||
name[baselen++] = tolower(c);
|
||||
}
|
||||
}
|
||||
|
||||
static int perf_parse_file(config_fn_t fn, void *data)
|
||||
{
|
||||
int comment = 0;
|
||||
int baselen = 0;
|
||||
static char var[MAXNAME];
|
||||
|
||||
/* U+FEFF Byte Order Mark in UTF8 */
|
||||
static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
|
||||
const unsigned char *bomptr = utf8_bom;
|
||||
|
||||
for (;;) {
|
||||
int c = get_next_char();
|
||||
if (bomptr && *bomptr) {
|
||||
/* We are at the file beginning; skip UTF8-encoded BOM
|
||||
* if present. Sane editors won't put this in on their
|
||||
* own, but e.g. Windows Notepad will do it happily. */
|
||||
if ((unsigned char) c == *bomptr) {
|
||||
bomptr++;
|
||||
continue;
|
||||
} else {
|
||||
/* Do not tolerate partial BOM. */
|
||||
if (bomptr != utf8_bom)
|
||||
break;
|
||||
/* No BOM at file beginning. Cool. */
|
||||
bomptr = NULL;
|
||||
}
|
||||
}
|
||||
if (c == '\n') {
|
||||
if (config_file_eof)
|
||||
return 0;
|
||||
comment = 0;
|
||||
continue;
|
||||
}
|
||||
if (comment || isspace(c))
|
||||
continue;
|
||||
if (c == '#' || c == ';') {
|
||||
comment = 1;
|
||||
continue;
|
||||
}
|
||||
if (c == '[') {
|
||||
baselen = get_base_var(var);
|
||||
if (baselen <= 0)
|
||||
break;
|
||||
var[baselen++] = '.';
|
||||
var[baselen] = 0;
|
||||
continue;
|
||||
}
|
||||
if (!isalpha(c))
|
||||
break;
|
||||
var[baselen] = tolower(c);
|
||||
if (get_value(fn, data, var, baselen+1) < 0)
|
||||
break;
|
||||
}
|
||||
die("bad config file line %d in %s", config_linenr, config_file_name);
|
||||
}
|
||||
|
||||
static int parse_unit_factor(const char *end, unsigned long *val)
|
||||
{
|
||||
if (!*end)
|
||||
return 1;
|
||||
else if (!strcasecmp(end, "k")) {
|
||||
*val *= 1024;
|
||||
return 1;
|
||||
}
|
||||
else if (!strcasecmp(end, "m")) {
|
||||
*val *= 1024 * 1024;
|
||||
return 1;
|
||||
}
|
||||
else if (!strcasecmp(end, "g")) {
|
||||
*val *= 1024 * 1024 * 1024;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_parse_long(const char *value, long *ret)
|
||||
{
|
||||
if (value && *value) {
|
||||
char *end;
|
||||
long val = strtol(value, &end, 0);
|
||||
unsigned long factor = 1;
|
||||
if (!parse_unit_factor(end, &factor))
|
||||
return 0;
|
||||
*ret = val * factor;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_parse_ulong(const char *value, unsigned long *ret)
|
||||
{
|
||||
if (value && *value) {
|
||||
char *end;
|
||||
unsigned long val = strtoul(value, &end, 0);
|
||||
if (!parse_unit_factor(end, &val))
|
||||
return 0;
|
||||
*ret = val;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void die_bad_config(const char *name)
|
||||
{
|
||||
if (config_file_name)
|
||||
die("bad config value for '%s' in %s", name, config_file_name);
|
||||
die("bad config value for '%s'", name);
|
||||
}
|
||||
|
||||
int perf_config_int(const char *name, const char *value)
|
||||
{
|
||||
long ret = 0;
|
||||
if (!perf_parse_long(value, &ret))
|
||||
die_bad_config(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long perf_config_ulong(const char *name, const char *value)
|
||||
{
|
||||
unsigned long ret;
|
||||
if (!perf_parse_ulong(value, &ret))
|
||||
die_bad_config(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
|
||||
{
|
||||
*is_bool = 1;
|
||||
if (!value)
|
||||
return 1;
|
||||
if (!*value)
|
||||
return 0;
|
||||
if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
|
||||
return 1;
|
||||
if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
|
||||
return 0;
|
||||
*is_bool = 0;
|
||||
return perf_config_int(name, value);
|
||||
}
|
||||
|
||||
int perf_config_bool(const char *name, const char *value)
|
||||
{
|
||||
int discard;
|
||||
return !!perf_config_bool_or_int(name, value, &discard);
|
||||
}
|
||||
|
||||
int perf_config_string(const char **dest, const char *var, const char *value)
|
||||
{
|
||||
if (!value)
|
||||
return config_error_nonbool(var);
|
||||
*dest = strdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_default_core_config(const char *var, const char *value)
|
||||
{
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_default_config(const char *var, const char *value, void *dummy)
|
||||
{
|
||||
if (!prefixcmp(var, "core."))
|
||||
return perf_default_core_config(var, value);
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
|
||||
{
|
||||
int ret;
|
||||
FILE *f = fopen(filename, "r");
|
||||
|
||||
ret = -1;
|
||||
if (f) {
|
||||
config_file = f;
|
||||
config_file_name = filename;
|
||||
config_linenr = 1;
|
||||
config_file_eof = 0;
|
||||
ret = perf_parse_file(fn, data);
|
||||
fclose(f);
|
||||
config_file_name = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *perf_etc_perfconfig(void)
|
||||
{
|
||||
static const char *system_wide;
|
||||
if (!system_wide)
|
||||
system_wide = system_path(ETC_PERFCONFIG);
|
||||
return system_wide;
|
||||
}
|
||||
|
||||
static int perf_env_bool(const char *k, int def)
|
||||
{
|
||||
const char *v = getenv(k);
|
||||
return v ? perf_config_bool(k, v) : def;
|
||||
}
|
||||
|
||||
int perf_config_system(void)
|
||||
{
|
||||
return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
|
||||
}
|
||||
|
||||
int perf_config_global(void)
|
||||
{
|
||||
return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
|
||||
}
|
||||
|
||||
int perf_config(config_fn_t fn, void *data)
|
||||
{
|
||||
int ret = 0, found = 0;
|
||||
char *repo_config = NULL;
|
||||
const char *home = NULL;
|
||||
|
||||
/* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
|
||||
if (config_exclusive_filename)
|
||||
return perf_config_from_file(fn, config_exclusive_filename, data);
|
||||
if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
|
||||
ret += perf_config_from_file(fn, perf_etc_perfconfig(),
|
||||
data);
|
||||
found += 1;
|
||||
}
|
||||
|
||||
home = getenv("HOME");
|
||||
if (perf_config_global() && home) {
|
||||
char *user_config = strdup(mkpath("%s/.perfconfig", home));
|
||||
if (!access(user_config, R_OK)) {
|
||||
ret += perf_config_from_file(fn, user_config, data);
|
||||
found += 1;
|
||||
}
|
||||
free(user_config);
|
||||
}
|
||||
|
||||
repo_config = perf_pathdup("config");
|
||||
if (!access(repo_config, R_OK)) {
|
||||
ret += perf_config_from_file(fn, repo_config, data);
|
||||
found += 1;
|
||||
}
|
||||
free(repo_config);
|
||||
if (found == 0)
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all the stuff for perf_config_set() below.
|
||||
*/
|
||||
|
||||
#define MAX_MATCHES 512
|
||||
|
||||
static struct {
|
||||
int baselen;
|
||||
char* key;
|
||||
int do_not_match;
|
||||
regex_t* value_regex;
|
||||
int multi_replace;
|
||||
size_t offset[MAX_MATCHES];
|
||||
enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
|
||||
int seen;
|
||||
} store;
|
||||
|
||||
static int matches(const char* key, const char* value)
|
||||
{
|
||||
return !strcmp(key, store.key) &&
|
||||
(store.value_regex == NULL ||
|
||||
(store.do_not_match ^
|
||||
!regexec(store.value_regex, value, 0, NULL, 0)));
|
||||
}
|
||||
|
||||
static int store_aux(const char* key, const char* value, void *cb)
|
||||
{
|
||||
const char *ep;
|
||||
size_t section_len;
|
||||
|
||||
switch (store.state) {
|
||||
case KEY_SEEN:
|
||||
if (matches(key, value)) {
|
||||
if (store.seen == 1 && store.multi_replace == 0) {
|
||||
warning("%s has multiple values", key);
|
||||
} else if (store.seen >= MAX_MATCHES) {
|
||||
error("too many matches for %s", key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
store.offset[store.seen] = ftell(config_file);
|
||||
store.seen++;
|
||||
}
|
||||
break;
|
||||
case SECTION_SEEN:
|
||||
/*
|
||||
* What we are looking for is in store.key (both
|
||||
* section and var), and its section part is baselen
|
||||
* long. We found key (again, both section and var).
|
||||
* We would want to know if this key is in the same
|
||||
* section as what we are looking for. We already
|
||||
* know we are in the same section as what should
|
||||
* hold store.key.
|
||||
*/
|
||||
ep = strrchr(key, '.');
|
||||
section_len = ep - key;
|
||||
|
||||
if ((section_len != store.baselen) ||
|
||||
memcmp(key, store.key, section_len+1)) {
|
||||
store.state = SECTION_END_SEEN;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not increment matches: this is no match, but we
|
||||
* just made sure we are in the desired section.
|
||||
*/
|
||||
store.offset[store.seen] = ftell(config_file);
|
||||
/* fallthru */
|
||||
case SECTION_END_SEEN:
|
||||
case START:
|
||||
if (matches(key, value)) {
|
||||
store.offset[store.seen] = ftell(config_file);
|
||||
store.state = KEY_SEEN;
|
||||
store.seen++;
|
||||
} else {
|
||||
if (strrchr(key, '.') - key == store.baselen &&
|
||||
!strncmp(key, store.key, store.baselen)) {
|
||||
store.state = SECTION_SEEN;
|
||||
store.offset[store.seen] = ftell(config_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_error(const char *filename)
|
||||
{
|
||||
error("failed to write new configuration file %s", filename);
|
||||
|
||||
/* Same error code as "failed to rename". */
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int store_write_section(int fd, const char* key)
|
||||
{
|
||||
const char *dot;
|
||||
int i, success;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
dot = memchr(key, '.', store.baselen);
|
||||
if (dot) {
|
||||
strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
|
||||
for (i = dot - key + 1; i < store.baselen; i++) {
|
||||
if (key[i] == '"' || key[i] == '\\')
|
||||
strbuf_addch(&sb, '\\');
|
||||
strbuf_addch(&sb, key[i]);
|
||||
}
|
||||
strbuf_addstr(&sb, "\"]\n");
|
||||
} else {
|
||||
strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
|
||||
}
|
||||
|
||||
success = write_in_full(fd, sb.buf, sb.len) == sb.len;
|
||||
strbuf_release(&sb);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static int store_write_pair(int fd, const char* key, const char* value)
|
||||
{
|
||||
int i, success;
|
||||
int length = strlen(key + store.baselen + 1);
|
||||
const char *quote = "";
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
|
||||
/*
|
||||
* Check to see if the value needs to be surrounded with a dq pair.
|
||||
* Note that problematic characters are always backslash-quoted; this
|
||||
* check is about not losing leading or trailing SP and strings that
|
||||
* follow beginning-of-comment characters (i.e. ';' and '#') by the
|
||||
* configuration parser.
|
||||
*/
|
||||
if (value[0] == ' ')
|
||||
quote = "\"";
|
||||
for (i = 0; value[i]; i++)
|
||||
if (value[i] == ';' || value[i] == '#')
|
||||
quote = "\"";
|
||||
if (i && value[i - 1] == ' ')
|
||||
quote = "\"";
|
||||
|
||||
strbuf_addf(&sb, "\t%.*s = %s",
|
||||
length, key + store.baselen + 1, quote);
|
||||
|
||||
for (i = 0; value[i]; i++)
|
||||
switch (value[i]) {
|
||||
case '\n':
|
||||
strbuf_addstr(&sb, "\\n");
|
||||
break;
|
||||
case '\t':
|
||||
strbuf_addstr(&sb, "\\t");
|
||||
break;
|
||||
case '"':
|
||||
case '\\':
|
||||
strbuf_addch(&sb, '\\');
|
||||
default:
|
||||
strbuf_addch(&sb, value[i]);
|
||||
break;
|
||||
}
|
||||
strbuf_addf(&sb, "%s\n", quote);
|
||||
|
||||
success = write_in_full(fd, sb.buf, sb.len) == sb.len;
|
||||
strbuf_release(&sb);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static ssize_t find_beginning_of_line(const char* contents, size_t size,
|
||||
size_t offset_, int* found_bracket)
|
||||
{
|
||||
size_t equal_offset = size, bracket_offset = size;
|
||||
ssize_t offset;
|
||||
|
||||
contline:
|
||||
for (offset = offset_-2; offset > 0
|
||||
&& contents[offset] != '\n'; offset--)
|
||||
switch (contents[offset]) {
|
||||
case '=': equal_offset = offset; break;
|
||||
case ']': bracket_offset = offset; break;
|
||||
}
|
||||
if (offset > 0 && contents[offset-1] == '\\') {
|
||||
offset_ = offset;
|
||||
goto contline;
|
||||
}
|
||||
if (bracket_offset < equal_offset) {
|
||||
*found_bracket = 1;
|
||||
offset = bracket_offset+1;
|
||||
} else
|
||||
offset++;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int perf_config_set(const char* key, const char* value)
|
||||
{
|
||||
return perf_config_set_multivar(key, value, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If value==NULL, unset in (remove from) config,
|
||||
* if value_regex!=NULL, disregard key/value pairs where value does not match.
|
||||
* if multi_replace==0, nothing, or only one matching key/value is replaced,
|
||||
* else all matching key/values (regardless how many) are removed,
|
||||
* before the new pair is written.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
* This function does this:
|
||||
*
|
||||
* - it locks the config file by creating ".perf/config.lock"
|
||||
*
|
||||
* - it then parses the config using store_aux() as validator to find
|
||||
* the position on the key/value pair to replace. If it is to be unset,
|
||||
* it must be found exactly once.
|
||||
*
|
||||
* - the config file is mmap()ed and the part before the match (if any) is
|
||||
* written to the lock file, then the changed part and the rest.
|
||||
*
|
||||
* - the config file is removed and the lock file rename()d to it.
|
||||
*
|
||||
*/
|
||||
int perf_config_set_multivar(const char* key, const char* value,
|
||||
const char* value_regex, int multi_replace)
|
||||
{
|
||||
int i, dot;
|
||||
int fd = -1, in_fd;
|
||||
int ret;
|
||||
char* config_filename;
|
||||
const char* last_dot = strrchr(key, '.');
|
||||
|
||||
if (config_exclusive_filename)
|
||||
config_filename = strdup(config_exclusive_filename);
|
||||
else
|
||||
config_filename = perf_pathdup("config");
|
||||
|
||||
/*
|
||||
* Since "key" actually contains the section name and the real
|
||||
* key name separated by a dot, we have to know where the dot is.
|
||||
*/
|
||||
|
||||
if (last_dot == NULL) {
|
||||
error("key does not contain a section: %s", key);
|
||||
ret = 2;
|
||||
goto out_free;
|
||||
}
|
||||
store.baselen = last_dot - key;
|
||||
|
||||
store.multi_replace = multi_replace;
|
||||
|
||||
/*
|
||||
* Validate the key and while at it, lower case it for matching.
|
||||
*/
|
||||
store.key = malloc(strlen(key) + 1);
|
||||
dot = 0;
|
||||
for (i = 0; key[i]; i++) {
|
||||
unsigned char c = key[i];
|
||||
if (c == '.')
|
||||
dot = 1;
|
||||
/* Leave the extended basename untouched.. */
|
||||
if (!dot || i > store.baselen) {
|
||||
if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
|
||||
error("invalid key: %s", key);
|
||||
free(store.key);
|
||||
ret = 1;
|
||||
goto out_free;
|
||||
}
|
||||
c = tolower(c);
|
||||
} else if (c == '\n') {
|
||||
error("invalid key (newline): %s", key);
|
||||
free(store.key);
|
||||
ret = 1;
|
||||
goto out_free;
|
||||
}
|
||||
store.key[i] = c;
|
||||
}
|
||||
store.key[i] = 0;
|
||||
|
||||
/*
|
||||
* If .perf/config does not exist yet, write a minimal version.
|
||||
*/
|
||||
in_fd = open(config_filename, O_RDONLY);
|
||||
if ( in_fd < 0 ) {
|
||||
free(store.key);
|
||||
|
||||
if ( ENOENT != errno ) {
|
||||
error("opening %s: %s", config_filename,
|
||||
strerror(errno));
|
||||
ret = 3; /* same as "invalid config file" */
|
||||
goto out_free;
|
||||
}
|
||||
/* if nothing to unset, error out */
|
||||
if (value == NULL) {
|
||||
ret = 5;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
store.key = (char*)key;
|
||||
if (!store_write_section(fd, key) ||
|
||||
!store_write_pair(fd, key, value))
|
||||
goto write_err_out;
|
||||
} else {
|
||||
struct stat st;
|
||||
char* contents;
|
||||
size_t contents_sz, copy_begin, copy_end;
|
||||
int i, new_line = 0;
|
||||
|
||||
if (value_regex == NULL)
|
||||
store.value_regex = NULL;
|
||||
else {
|
||||
if (value_regex[0] == '!') {
|
||||
store.do_not_match = 1;
|
||||
value_regex++;
|
||||
} else
|
||||
store.do_not_match = 0;
|
||||
|
||||
store.value_regex = (regex_t*)malloc(sizeof(regex_t));
|
||||
if (regcomp(store.value_regex, value_regex,
|
||||
REG_EXTENDED)) {
|
||||
error("invalid pattern: %s", value_regex);
|
||||
free(store.value_regex);
|
||||
ret = 6;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
store.offset[0] = 0;
|
||||
store.state = START;
|
||||
store.seen = 0;
|
||||
|
||||
/*
|
||||
* After this, store.offset will contain the *end* offset
|
||||
* of the last match, or remain at 0 if no match was found.
|
||||
* As a side effect, we make sure to transform only a valid
|
||||
* existing config file.
|
||||
*/
|
||||
if (perf_config_from_file(store_aux, config_filename, NULL)) {
|
||||
error("invalid config file %s", config_filename);
|
||||
free(store.key);
|
||||
if (store.value_regex != NULL) {
|
||||
regfree(store.value_regex);
|
||||
free(store.value_regex);
|
||||
}
|
||||
ret = 3;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
free(store.key);
|
||||
if (store.value_regex != NULL) {
|
||||
regfree(store.value_regex);
|
||||
free(store.value_regex);
|
||||
}
|
||||
|
||||
/* if nothing to unset, or too many matches, error out */
|
||||
if ((store.seen == 0 && value == NULL) ||
|
||||
(store.seen > 1 && multi_replace == 0)) {
|
||||
ret = 5;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
fstat(in_fd, &st);
|
||||
contents_sz = xsize_t(st.st_size);
|
||||
contents = mmap(NULL, contents_sz, PROT_READ,
|
||||
MAP_PRIVATE, in_fd, 0);
|
||||
close(in_fd);
|
||||
|
||||
if (store.seen == 0)
|
||||
store.seen = 1;
|
||||
|
||||
for (i = 0, copy_begin = 0; i < store.seen; i++) {
|
||||
if (store.offset[i] == 0) {
|
||||
store.offset[i] = copy_end = contents_sz;
|
||||
} else if (store.state != KEY_SEEN) {
|
||||
copy_end = store.offset[i];
|
||||
} else
|
||||
copy_end = find_beginning_of_line(
|
||||
contents, contents_sz,
|
||||
store.offset[i]-2, &new_line);
|
||||
|
||||
if (copy_end > 0 && contents[copy_end-1] != '\n')
|
||||
new_line = 1;
|
||||
|
||||
/* write the first part of the config */
|
||||
if (copy_end > copy_begin) {
|
||||
if (write_in_full(fd, contents + copy_begin,
|
||||
copy_end - copy_begin) <
|
||||
copy_end - copy_begin)
|
||||
goto write_err_out;
|
||||
if (new_line &&
|
||||
write_in_full(fd, "\n", 1) != 1)
|
||||
goto write_err_out;
|
||||
}
|
||||
copy_begin = store.offset[i];
|
||||
}
|
||||
|
||||
/* write the pair (value == NULL means unset) */
|
||||
if (value != NULL) {
|
||||
if (store.state == START) {
|
||||
if (!store_write_section(fd, key))
|
||||
goto write_err_out;
|
||||
}
|
||||
if (!store_write_pair(fd, key, value))
|
||||
goto write_err_out;
|
||||
}
|
||||
|
||||
/* write the rest of the config */
|
||||
if (copy_begin < contents_sz)
|
||||
if (write_in_full(fd, contents + copy_begin,
|
||||
contents_sz - copy_begin) <
|
||||
contents_sz - copy_begin)
|
||||
goto write_err_out;
|
||||
|
||||
munmap(contents, contents_sz);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out_free:
|
||||
free(config_filename);
|
||||
return ret;
|
||||
|
||||
write_err_out:
|
||||
goto out_free;
|
||||
|
||||
}
|
||||
|
||||
static int section_name_match (const char *buf, const char *name)
|
||||
{
|
||||
int i = 0, j = 0, dot = 0;
|
||||
for (; buf[i] && buf[i] != ']'; i++) {
|
||||
if (!dot && isspace(buf[i])) {
|
||||
dot = 1;
|
||||
if (name[j++] != '.')
|
||||
break;
|
||||
for (i++; isspace(buf[i]); i++)
|
||||
; /* do nothing */
|
||||
if (buf[i] != '"')
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if (buf[i] == '\\' && dot)
|
||||
i++;
|
||||
else if (buf[i] == '"' && dot) {
|
||||
for (i++; isspace(buf[i]); i++)
|
||||
; /* do_nothing */
|
||||
break;
|
||||
}
|
||||
if (buf[i] != name[j++])
|
||||
break;
|
||||
}
|
||||
return (buf[i] == ']' && name[j] == 0);
|
||||
}
|
||||
|
||||
/* if new_name == NULL, the section is removed instead */
|
||||
int perf_config_rename_section(const char *old_name, const char *new_name)
|
||||
{
|
||||
int ret = 0, remove = 0;
|
||||
char *config_filename;
|
||||
int out_fd;
|
||||
char buf[1024];
|
||||
|
||||
if (config_exclusive_filename)
|
||||
config_filename = strdup(config_exclusive_filename);
|
||||
else
|
||||
config_filename = perf_pathdup("config");
|
||||
if (out_fd < 0) {
|
||||
ret = error("could not lock config file %s", config_filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(config_file = fopen(config_filename, "rb"))) {
|
||||
/* no config file means nothing to rename, no error */
|
||||
goto unlock_and_out;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), config_file)) {
|
||||
int i;
|
||||
int length;
|
||||
for (i = 0; buf[i] && isspace(buf[i]); i++)
|
||||
; /* do nothing */
|
||||
if (buf[i] == '[') {
|
||||
/* it's a section */
|
||||
if (section_name_match (&buf[i+1], old_name)) {
|
||||
ret++;
|
||||
if (new_name == NULL) {
|
||||
remove = 1;
|
||||
continue;
|
||||
}
|
||||
store.baselen = strlen(new_name);
|
||||
if (!store_write_section(out_fd, new_name)) {
|
||||
goto out;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
remove = 0;
|
||||
}
|
||||
if (remove)
|
||||
continue;
|
||||
length = strlen(buf);
|
||||
if (write_in_full(out_fd, buf, length) != length) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
fclose(config_file);
|
||||
unlock_and_out:
|
||||
out:
|
||||
free(config_filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this to report error for your variable that should not
|
||||
* get a boolean value (i.e. "[my] var" means "true").
|
||||
*/
|
||||
int config_error_nonbool(const char *var)
|
||||
{
|
||||
return error("Missing value for '%s'", var);
|
||||
}
|
||||
26
Documentation/perf_counter/ctype.c
Normal file
26
Documentation/perf_counter/ctype.c
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Sane locale-independent, ASCII ctype.
|
||||
*
|
||||
* No surprises, and works with signed and unsigned chars.
|
||||
*/
|
||||
#include "cache.h"
|
||||
|
||||
enum {
|
||||
S = GIT_SPACE,
|
||||
A = GIT_ALPHA,
|
||||
D = GIT_DIGIT,
|
||||
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
|
||||
R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */
|
||||
};
|
||||
|
||||
unsigned char sane_ctype[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
|
||||
S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0, /* 32.. 47 */
|
||||
D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G, /* 48.. 63 */
|
||||
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
|
||||
A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0, /* 80.. 95 */
|
||||
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
|
||||
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0, /* 112..127 */
|
||||
/* Nothing in the 128.. range */
|
||||
};
|
||||
165
Documentation/perf_counter/exec_cmd.c
Normal file
165
Documentation/perf_counter/exec_cmd.c
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
#include "cache.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "quote.h"
|
||||
#define MAX_ARGS 32
|
||||
|
||||
extern char **environ;
|
||||
static const char *argv_exec_path;
|
||||
static const char *argv0_path;
|
||||
|
||||
const char *system_path(const char *path)
|
||||
{
|
||||
#ifdef RUNTIME_PREFIX
|
||||
static const char *prefix;
|
||||
#else
|
||||
static const char *prefix = PREFIX;
|
||||
#endif
|
||||
struct strbuf d = STRBUF_INIT;
|
||||
|
||||
if (is_absolute_path(path))
|
||||
return path;
|
||||
|
||||
#ifdef RUNTIME_PREFIX
|
||||
assert(argv0_path);
|
||||
assert(is_absolute_path(argv0_path));
|
||||
|
||||
if (!prefix &&
|
||||
!(prefix = strip_path_suffix(argv0_path, PERF_EXEC_PATH)) &&
|
||||
!(prefix = strip_path_suffix(argv0_path, BINDIR)) &&
|
||||
!(prefix = strip_path_suffix(argv0_path, "perf"))) {
|
||||
prefix = PREFIX;
|
||||
fprintf(stderr, "RUNTIME_PREFIX requested, "
|
||||
"but prefix computation failed. "
|
||||
"Using static fallback '%s'.\n", prefix);
|
||||
}
|
||||
#endif
|
||||
|
||||
strbuf_addf(&d, "%s/%s", prefix, path);
|
||||
path = strbuf_detach(&d, NULL);
|
||||
return path;
|
||||
}
|
||||
|
||||
const char *perf_extract_argv0_path(const char *argv0)
|
||||
{
|
||||
const char *slash;
|
||||
|
||||
if (!argv0 || !*argv0)
|
||||
return NULL;
|
||||
slash = argv0 + strlen(argv0);
|
||||
|
||||
while (argv0 <= slash && !is_dir_sep(*slash))
|
||||
slash--;
|
||||
|
||||
if (slash >= argv0) {
|
||||
argv0_path = strndup(argv0, slash - argv0);
|
||||
return slash + 1;
|
||||
}
|
||||
|
||||
return argv0;
|
||||
}
|
||||
|
||||
void perf_set_argv_exec_path(const char *exec_path)
|
||||
{
|
||||
argv_exec_path = exec_path;
|
||||
/*
|
||||
* Propagate this setting to external programs.
|
||||
*/
|
||||
setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
|
||||
}
|
||||
|
||||
|
||||
/* Returns the highest-priority, location to look for perf programs. */
|
||||
const char *perf_exec_path(void)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
if (argv_exec_path)
|
||||
return argv_exec_path;
|
||||
|
||||
env = getenv(EXEC_PATH_ENVIRONMENT);
|
||||
if (env && *env) {
|
||||
return env;
|
||||
}
|
||||
|
||||
return system_path(PERF_EXEC_PATH);
|
||||
}
|
||||
|
||||
static void add_path(struct strbuf *out, const char *path)
|
||||
{
|
||||
if (path && *path) {
|
||||
if (is_absolute_path(path))
|
||||
strbuf_addstr(out, path);
|
||||
else
|
||||
strbuf_addstr(out, make_nonrelative_path(path));
|
||||
|
||||
strbuf_addch(out, PATH_SEP);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_path(void)
|
||||
{
|
||||
const char *old_path = getenv("PATH");
|
||||
struct strbuf new_path = STRBUF_INIT;
|
||||
|
||||
add_path(&new_path, perf_exec_path());
|
||||
add_path(&new_path, argv0_path);
|
||||
|
||||
if (old_path)
|
||||
strbuf_addstr(&new_path, old_path);
|
||||
else
|
||||
strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
|
||||
|
||||
setenv("PATH", new_path.buf, 1);
|
||||
|
||||
strbuf_release(&new_path);
|
||||
}
|
||||
|
||||
const char **prepare_perf_cmd(const char **argv)
|
||||
{
|
||||
int argc;
|
||||
const char **nargv;
|
||||
|
||||
for (argc = 0; argv[argc]; argc++)
|
||||
; /* just counting */
|
||||
nargv = malloc(sizeof(*nargv) * (argc + 2));
|
||||
|
||||
nargv[0] = "perf";
|
||||
for (argc = 0; argv[argc]; argc++)
|
||||
nargv[argc + 1] = argv[argc];
|
||||
nargv[argc + 1] = NULL;
|
||||
return nargv;
|
||||
}
|
||||
|
||||
int execv_perf_cmd(const char **argv) {
|
||||
const char **nargv = prepare_perf_cmd(argv);
|
||||
|
||||
/* execvp() can only ever return if it fails */
|
||||
execvp("perf", (char **)nargv);
|
||||
|
||||
free(nargv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int execl_perf_cmd(const char *cmd,...)
|
||||
{
|
||||
int argc;
|
||||
const char *argv[MAX_ARGS + 1];
|
||||
const char *arg;
|
||||
va_list param;
|
||||
|
||||
va_start(param, cmd);
|
||||
argv[0] = cmd;
|
||||
argc = 1;
|
||||
while (argc < MAX_ARGS) {
|
||||
arg = argv[argc++] = va_arg(param, char *);
|
||||
if (!arg)
|
||||
break;
|
||||
}
|
||||
va_end(param);
|
||||
if (MAX_ARGS <= argc)
|
||||
return error("too many args to run %s", cmd);
|
||||
|
||||
argv[argc] = NULL;
|
||||
return execv_perf_cmd(argv);
|
||||
}
|
||||
13
Documentation/perf_counter/exec_cmd.h
Normal file
13
Documentation/perf_counter/exec_cmd.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef PERF_EXEC_CMD_H
|
||||
#define PERF_EXEC_CMD_H
|
||||
|
||||
extern void perf_set_argv_exec_path(const char *exec_path);
|
||||
extern const char *perf_extract_argv0_path(const char *path);
|
||||
extern const char *perf_exec_path(void);
|
||||
extern void setup_path(void);
|
||||
extern const char **prepare_perf_cmd(const char **argv);
|
||||
extern int execv_perf_cmd(const char **argv); /* NULL terminated */
|
||||
extern int execl_perf_cmd(const char *cmd, ...);
|
||||
extern const char *system_path(const char *path);
|
||||
|
||||
#endif /* PERF_EXEC_CMD_H */
|
||||
24
Documentation/perf_counter/generate-cmdlist.sh
Executable file
24
Documentation/perf_counter/generate-cmdlist.sh
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo "/* Automatically generated by $0 */
|
||||
struct cmdname_help
|
||||
{
|
||||
char name[16];
|
||||
char help[80];
|
||||
};
|
||||
|
||||
static struct cmdname_help common_cmds[] = {"
|
||||
|
||||
sed -n -e 's/^git-\([^ ]*\)[ ].* common.*/\1/p' command-list.txt |
|
||||
sort |
|
||||
while read cmd
|
||||
do
|
||||
sed -n '
|
||||
/^NAME/,/git-'"$cmd"'/H
|
||||
${
|
||||
x
|
||||
s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
|
||||
p
|
||||
}' "Documentation/git-$cmd.txt"
|
||||
done
|
||||
echo "};"
|
||||
366
Documentation/perf_counter/help.c
Normal file
366
Documentation/perf_counter/help.c
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "levenshtein.h"
|
||||
#include "help.h"
|
||||
|
||||
/* most GUI terminals set COLUMNS (although some don't export it) */
|
||||
static int term_columns(void)
|
||||
{
|
||||
char *col_string = getenv("COLUMNS");
|
||||
int n_cols;
|
||||
|
||||
if (col_string && (n_cols = atoi(col_string)) > 0)
|
||||
return n_cols;
|
||||
|
||||
#ifdef TIOCGWINSZ
|
||||
{
|
||||
struct winsize ws;
|
||||
if (!ioctl(1, TIOCGWINSZ, &ws)) {
|
||||
if (ws.ws_col)
|
||||
return ws.ws_col;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 80;
|
||||
}
|
||||
|
||||
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
|
||||
{
|
||||
struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
|
||||
|
||||
ent->len = len;
|
||||
memcpy(ent->name, name, len);
|
||||
ent->name[len] = 0;
|
||||
|
||||
ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
|
||||
cmds->names[cmds->cnt++] = ent;
|
||||
}
|
||||
|
||||
static void clean_cmdnames(struct cmdnames *cmds)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < cmds->cnt; ++i)
|
||||
free(cmds->names[i]);
|
||||
free(cmds->names);
|
||||
cmds->cnt = 0;
|
||||
cmds->alloc = 0;
|
||||
}
|
||||
|
||||
static int cmdname_compare(const void *a_, const void *b_)
|
||||
{
|
||||
struct cmdname *a = *(struct cmdname **)a_;
|
||||
struct cmdname *b = *(struct cmdname **)b_;
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
static void uniq(struct cmdnames *cmds)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (!cmds->cnt)
|
||||
return;
|
||||
|
||||
for (i = j = 1; i < cmds->cnt; i++)
|
||||
if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
|
||||
cmds->names[j++] = cmds->names[i];
|
||||
|
||||
cmds->cnt = j;
|
||||
}
|
||||
|
||||
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
|
||||
{
|
||||
int ci, cj, ei;
|
||||
int cmp;
|
||||
|
||||
ci = cj = ei = 0;
|
||||
while (ci < cmds->cnt && ei < excludes->cnt) {
|
||||
cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
|
||||
if (cmp < 0)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
else if (cmp == 0)
|
||||
ci++, ei++;
|
||||
else if (cmp > 0)
|
||||
ei++;
|
||||
}
|
||||
|
||||
while (ci < cmds->cnt)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
|
||||
cmds->cnt = cj;
|
||||
}
|
||||
|
||||
static void pretty_print_string_list(struct cmdnames *cmds, int longest)
|
||||
{
|
||||
int cols = 1, rows;
|
||||
int space = longest + 1; /* min 1 SP between words */
|
||||
int max_cols = term_columns() - 1; /* don't print *on* the edge */
|
||||
int i, j;
|
||||
|
||||
if (space < max_cols)
|
||||
cols = max_cols / space;
|
||||
rows = (cmds->cnt + cols - 1) / cols;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
printf(" ");
|
||||
|
||||
for (j = 0; j < cols; j++) {
|
||||
int n = j * rows + i;
|
||||
int size = space;
|
||||
if (n >= cmds->cnt)
|
||||
break;
|
||||
if (j == cols-1 || n + rows >= cmds->cnt)
|
||||
size = 1;
|
||||
printf("%-*s", size, cmds->names[n]->name);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
static int is_executable(const char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(name, &st) || /* stat, not lstat */
|
||||
!S_ISREG(st.st_mode))
|
||||
return 0;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
/* cannot trust the executable bit, peek into the file instead */
|
||||
char buf[3] = { 0 };
|
||||
int n;
|
||||
int fd = open(name, O_RDONLY);
|
||||
st.st_mode &= ~S_IXUSR;
|
||||
if (fd >= 0) {
|
||||
n = read(fd, buf, 2);
|
||||
if (n == 2)
|
||||
/* DOS executables start with "MZ" */
|
||||
if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
|
||||
st.st_mode |= S_IXUSR;
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
return st.st_mode & S_IXUSR;
|
||||
}
|
||||
|
||||
static void list_commands_in_dir(struct cmdnames *cmds,
|
||||
const char *path,
|
||||
const char *prefix)
|
||||
{
|
||||
int prefix_len;
|
||||
DIR *dir = opendir(path);
|
||||
struct dirent *de;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int len;
|
||||
|
||||
if (!dir)
|
||||
return;
|
||||
if (!prefix)
|
||||
prefix = "perf-";
|
||||
prefix_len = strlen(prefix);
|
||||
|
||||
strbuf_addf(&buf, "%s/", path);
|
||||
len = buf.len;
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
int entlen;
|
||||
|
||||
if (prefixcmp(de->d_name, prefix))
|
||||
continue;
|
||||
|
||||
strbuf_setlen(&buf, len);
|
||||
strbuf_addstr(&buf, de->d_name);
|
||||
if (!is_executable(buf.buf))
|
||||
continue;
|
||||
|
||||
entlen = strlen(de->d_name) - prefix_len;
|
||||
if (has_extension(de->d_name, ".exe"))
|
||||
entlen -= 4;
|
||||
|
||||
add_cmdname(cmds, de->d_name + prefix_len, entlen);
|
||||
}
|
||||
closedir(dir);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
void load_command_list(const char *prefix,
|
||||
struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds)
|
||||
{
|
||||
const char *env_path = getenv("PATH");
|
||||
const char *exec_path = perf_exec_path();
|
||||
|
||||
if (exec_path) {
|
||||
list_commands_in_dir(main_cmds, exec_path, prefix);
|
||||
qsort(main_cmds->names, main_cmds->cnt,
|
||||
sizeof(*main_cmds->names), cmdname_compare);
|
||||
uniq(main_cmds);
|
||||
}
|
||||
|
||||
if (env_path) {
|
||||
char *paths, *path, *colon;
|
||||
path = paths = strdup(env_path);
|
||||
while (1) {
|
||||
if ((colon = strchr(path, PATH_SEP)))
|
||||
*colon = 0;
|
||||
if (!exec_path || strcmp(path, exec_path))
|
||||
list_commands_in_dir(other_cmds, path, prefix);
|
||||
|
||||
if (!colon)
|
||||
break;
|
||||
path = colon + 1;
|
||||
}
|
||||
free(paths);
|
||||
|
||||
qsort(other_cmds->names, other_cmds->cnt,
|
||||
sizeof(*other_cmds->names), cmdname_compare);
|
||||
uniq(other_cmds);
|
||||
}
|
||||
exclude_cmds(other_cmds, main_cmds);
|
||||
}
|
||||
|
||||
void list_commands(const char *title, struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds)
|
||||
{
|
||||
int i, longest = 0;
|
||||
|
||||
for (i = 0; i < main_cmds->cnt; i++)
|
||||
if (longest < main_cmds->names[i]->len)
|
||||
longest = main_cmds->names[i]->len;
|
||||
for (i = 0; i < other_cmds->cnt; i++)
|
||||
if (longest < other_cmds->names[i]->len)
|
||||
longest = other_cmds->names[i]->len;
|
||||
|
||||
if (main_cmds->cnt) {
|
||||
const char *exec_path = perf_exec_path();
|
||||
printf("available %s in '%s'\n", title, exec_path);
|
||||
printf("----------------");
|
||||
mput_char('-', strlen(title) + strlen(exec_path));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(main_cmds, longest);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
if (other_cmds->cnt) {
|
||||
printf("%s available from elsewhere on your $PATH\n", title);
|
||||
printf("---------------------------------------");
|
||||
mput_char('-', strlen(title));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(other_cmds, longest);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
int is_in_cmdlist(struct cmdnames *c, const char *s)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < c->cnt; i++)
|
||||
if (!strcmp(s, c->names[i]->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autocorrect;
|
||||
static struct cmdnames aliases;
|
||||
|
||||
static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp(var, "help.autocorrect"))
|
||||
autocorrect = perf_config_int(var,value);
|
||||
/* Also use aliases for command lookup */
|
||||
if (!prefixcmp(var, "alias."))
|
||||
add_cmdname(&aliases, var + 6, strlen(var + 6));
|
||||
|
||||
return perf_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
static int levenshtein_compare(const void *p1, const void *p2)
|
||||
{
|
||||
const struct cmdname *const *c1 = p1, *const *c2 = p2;
|
||||
const char *s1 = (*c1)->name, *s2 = (*c2)->name;
|
||||
int l1 = (*c1)->len;
|
||||
int l2 = (*c2)->len;
|
||||
return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
|
||||
}
|
||||
|
||||
static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
|
||||
{
|
||||
int i;
|
||||
ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
|
||||
|
||||
for (i = 0; i < old->cnt; i++)
|
||||
cmds->names[cmds->cnt++] = old->names[i];
|
||||
free(old->names);
|
||||
old->cnt = 0;
|
||||
old->names = NULL;
|
||||
}
|
||||
|
||||
const char *help_unknown_cmd(const char *cmd)
|
||||
{
|
||||
int i, n, best_similarity = 0;
|
||||
struct cmdnames main_cmds, other_cmds;
|
||||
|
||||
memset(&main_cmds, 0, sizeof(main_cmds));
|
||||
memset(&other_cmds, 0, sizeof(main_cmds));
|
||||
memset(&aliases, 0, sizeof(aliases));
|
||||
|
||||
perf_config(perf_unknown_cmd_config, NULL);
|
||||
|
||||
load_command_list("perf-", &main_cmds, &other_cmds);
|
||||
|
||||
add_cmd_list(&main_cmds, &aliases);
|
||||
add_cmd_list(&main_cmds, &other_cmds);
|
||||
qsort(main_cmds.names, main_cmds.cnt,
|
||||
sizeof(main_cmds.names), cmdname_compare);
|
||||
uniq(&main_cmds);
|
||||
|
||||
/* This reuses cmdname->len for similarity index */
|
||||
for (i = 0; i < main_cmds.cnt; ++i)
|
||||
main_cmds.names[i]->len =
|
||||
levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
|
||||
|
||||
qsort(main_cmds.names, main_cmds.cnt,
|
||||
sizeof(*main_cmds.names), levenshtein_compare);
|
||||
|
||||
if (!main_cmds.cnt)
|
||||
die ("Uh oh. Your system reports no Git commands at all.");
|
||||
|
||||
best_similarity = main_cmds.names[0]->len;
|
||||
n = 1;
|
||||
while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
|
||||
++n;
|
||||
if (autocorrect && n == 1) {
|
||||
const char *assumed = main_cmds.names[0]->name;
|
||||
main_cmds.names[0] = NULL;
|
||||
clean_cmdnames(&main_cmds);
|
||||
fprintf(stderr, "WARNING: You called a Git program named '%s', "
|
||||
"which does not exist.\n"
|
||||
"Continuing under the assumption that you meant '%s'\n",
|
||||
cmd, assumed);
|
||||
if (autocorrect > 0) {
|
||||
fprintf(stderr, "in %0.1f seconds automatically...\n",
|
||||
(float)autocorrect/10.0);
|
||||
poll(NULL, 0, autocorrect * 100);
|
||||
}
|
||||
return assumed;
|
||||
}
|
||||
|
||||
fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
|
||||
|
||||
if (best_similarity < 6) {
|
||||
fprintf(stderr, "\nDid you mean %s?\n",
|
||||
n < 2 ? "this": "one of these");
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int cmd_version(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
printf("perf version %s\n", perf_version_string);
|
||||
return 0;
|
||||
}
|
||||
29
Documentation/perf_counter/help.h
Normal file
29
Documentation/perf_counter/help.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef HELP_H
|
||||
#define HELP_H
|
||||
|
||||
struct cmdnames {
|
||||
int alloc;
|
||||
int cnt;
|
||||
struct cmdname {
|
||||
size_t len; /* also used for similarity index in help.c */
|
||||
char name[FLEX_ARRAY];
|
||||
} **names;
|
||||
};
|
||||
|
||||
static inline void mput_char(char c, unsigned int num)
|
||||
{
|
||||
while(num--)
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
void load_command_list(const char *prefix,
|
||||
struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds);
|
||||
void add_cmdname(struct cmdnames *cmds, const char *name, int len);
|
||||
/* Here we require that excludes is a sorted list. */
|
||||
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
|
||||
int is_in_cmdlist(struct cmdnames *c, const char *s);
|
||||
void list_commands(const char *title, struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds);
|
||||
|
||||
#endif /* HELP_H */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue