Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
49e70dd
Documentation
arch
block
crypto
drivers
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools/perf
Documentation
util
include
PERF-VERSION-GEN
abspath.c
alias.c
cache.h
callchain.c
callchain.h
color.c
color.h
config.c
ctype.c
debug.c
debug.h
environment.c
event.h
exec_cmd.c
exec_cmd.h
generate-cmdlist.sh
header.c
header.h
help.c
help.h
levenshtein.c
levenshtein.h
map.c
module.c
module.h
pager.c
parse-events.c
parse-events.h
parse-options.c
parse-options.h
path.c
quote.c
quote.h
run-command.c
run-command.h
sigchain.c
sigchain.h
strbuf.c
strbuf.h
string.c
string.h
strlist.c
strlist.h
svghelper.c
svghelper.h
symbol.c
symbol.h
thread.c
thread.h
trace-event-info.c
trace-event-parse.c
trace-event-read.c
trace-event.h
types.h
usage.c
util.h
values.c
values.h
wrapper.c
.gitignore
CREDITS
Makefile
builtin-annotate.c
builtin-help.c
builtin-list.c
builtin-record.c
builtin-report.c
builtin-sched.c
builtin-stat.c
builtin-timechart.c
builtin-top.c
builtin-trace.c
builtin.h
command-list.txt
design.txt
perf.c
perf.h
usr
virt
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
tools
/
perf
/
util
/
parse-events.c
Blame
Blame
Latest commit
History
History
843 lines (696 loc) · 18.4 KB
Breadcrumbs
linux
/
tools
/
perf
/
util
/
parse-events.c
Top
File metadata and controls
Code
Blame
843 lines (696 loc) · 18.4 KB
Raw
#include "util.h" #include "../perf.h" #include "parse-options.h" #include "parse-events.h" #include "exec_cmd.h" #include "string.h" #include "cache.h" #include "header.h" int nr_counters; struct perf_event_attr attrs[MAX_COUNTERS]; struct event_symbol { u8 type; u64 config; const char *symbol; const char *alias; }; enum event_result { EVT_FAILED, EVT_HANDLED, EVT_HANDLED_ALL }; char debugfs_path[MAXPATHLEN]; #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x static struct event_symbol event_symbols[] = { { CHW(CPU_CYCLES), "cpu-cycles", "cycles" }, { CHW(INSTRUCTIONS), "instructions", "" }, { CHW(CACHE_REFERENCES), "cache-references", "" }, { CHW(CACHE_MISSES), "cache-misses", "" }, { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" }, { CHW(BRANCH_MISSES), "branch-misses", "" }, { CHW(BUS_CYCLES), "bus-cycles", "" }, { CSW(CPU_CLOCK), "cpu-clock", "" }, { CSW(TASK_CLOCK), "task-clock", "" }, { CSW(PAGE_FAULTS), "page-faults", "faults" }, { CSW(PAGE_FAULTS_MIN), "minor-faults", "" }, { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, }; #define __PERF_EVENT_FIELD(config, name) \ ((config & PERF_EVENT_##name##_MASK) >> PERF_EVENT_##name##_SHIFT) #define PERF_EVENT_RAW(config) __PERF_EVENT_FIELD(config, RAW) #define PERF_EVENT_CONFIG(config) __PERF_EVENT_FIELD(config, CONFIG) #define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE) #define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT) static const char *hw_event_names[] = { "cycles", "instructions", "cache-references", "cache-misses", "branches", "branch-misses", "bus-cycles", }; static const char *sw_event_names[] = { "cpu-clock-msecs", "task-clock-msecs", "page-faults", "context-switches", "CPU-migrations", "minor-faults", "major-faults", }; #define MAX_ALIASES 8 static const char *hw_cache[][MAX_ALIASES] = { { "L1-dcache", "l1-d", "l1d", "L1-data", }, { "L1-icache", "l1-i", "l1i", "L1-instruction", }, { "LLC", "L2" }, { "dTLB", "d-tlb", "Data-TLB", }, { "iTLB", "i-tlb", "Instruction-TLB", }, { "branch", "branches", "bpu", "btb", "bpc", }, }; static const char *hw_cache_op[][MAX_ALIASES] = { { "load", "loads", "read", }, { "store", "stores", "write", }, { "prefetch", "prefetches", "speculative-read", "speculative-load", }, }; static const char *hw_cache_result[][MAX_ALIASES] = { { "refs", "Reference", "ops", "access", }, { "misses", "miss", }, }; #define C(x) PERF_COUNT_HW_CACHE_##x #define CACHE_READ (1 << C(OP_READ)) #define CACHE_WRITE (1 << C(OP_WRITE)) #define CACHE_PREFETCH (1 << C(OP_PREFETCH)) #define COP(x) (1 << x) /* * cache operartion stat * L1I : Read and prefetch only * ITLB and BPU : Read-only */ static unsigned long hw_cache_stat[C(MAX)] = { [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), [C(L1I)] = (CACHE_READ | CACHE_PREFETCH), [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), [C(ITLB)] = (CACHE_READ), [C(BPU)] = (CACHE_READ), }; #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ if (sys_dirent.d_type == DT_DIR && \ (strcmp(sys_dirent.d_name, ".")) && \ (strcmp(sys_dirent.d_name, ".."))) static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) { char evt_path[MAXPATHLEN]; int fd; snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_dir->d_name, evt_dir->d_name); fd = open(evt_path, O_RDONLY); if (fd < 0) return -EINVAL; close(fd); return 0; } #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \ while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ if (evt_dirent.d_type == DT_DIR && \ (strcmp(evt_dirent.d_name, ".")) && \ (strcmp(evt_dirent.d_name, "..")) && \ (!tp_event_has_id(&sys_dirent, &evt_dirent))) #define MAX_EVENT_LENGTH 512 int valid_debugfs_mount(const char *debugfs) { struct statfs st_fs; if (statfs(debugfs, &st_fs) < 0) return -ENOENT; else if (st_fs.f_type != (long) DEBUGFS_MAGIC) return -ENOENT; return 0; } struct tracepoint_path *tracepoint_id_to_path(u64 config) { struct tracepoint_path *path = NULL; DIR *sys_dir, *evt_dir; struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; char id_buf[4]; int fd; u64 id; char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; if (valid_debugfs_mount(debugfs_path)) return NULL; sys_dir = opendir(debugfs_path); if (!sys_dir) return NULL; for_each_subsystem(sys_dir, sys_dirent, sys_next) { snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_dirent.d_name); evt_dir = opendir(dir_path); if (!evt_dir) continue; for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path, evt_dirent.d_name); fd = open(evt_path, O_RDONLY); if (fd < 0) continue; if (read(fd, id_buf, sizeof(id_buf)) < 0) { close(fd); continue; } close(fd); id = atoll(id_buf); if (id == config) { closedir(evt_dir); closedir(sys_dir); path = calloc(1, sizeof(path)); path->system = malloc(MAX_EVENT_LENGTH); if (!path->system) { free(path); return NULL; } path->name = malloc(MAX_EVENT_LENGTH); if (!path->name) { free(path->system); free(path); return NULL; } strncpy(path->system, sys_dirent.d_name, MAX_EVENT_LENGTH); strncpy(path->name, evt_dirent.d_name, MAX_EVENT_LENGTH); return path; } } closedir(evt_dir); } closedir(sys_dir); return NULL; } #define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1) static const char *tracepoint_id_to_name(u64 config) { static char buf[TP_PATH_LEN]; struct tracepoint_path *path; path = tracepoint_id_to_path(config); if (path) { snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name); free(path->name); free(path->system); free(path); } else snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown"); return buf; } static int is_cache_op_valid(u8 cache_type, u8 cache_op) { if (hw_cache_stat[cache_type] & COP(cache_op)) return 1; /* valid */ else return 0; /* invalid */ } static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) { static char name[50]; if (cache_result) { sprintf(name, "%s-%s-%s", hw_cache[cache_type][0], hw_cache_op[cache_op][0], hw_cache_result[cache_result][0]); } else { sprintf(name, "%s-%s", hw_cache[cache_type][0], hw_cache_op[cache_op][1]); } return name; } const char *event_name(int counter) { u64 config = attrs[counter].config; int type = attrs[counter].type; return __event_name(type, config); } const char *__event_name(int type, u64 config) { static char buf[32]; if (type == PERF_TYPE_RAW) { sprintf(buf, "raw 0x%llx", config); return buf; } switch (type) { case PERF_TYPE_HARDWARE: if (config < PERF_COUNT_HW_MAX) return hw_event_names[config]; return "unknown-hardware"; case PERF_TYPE_HW_CACHE: { u8 cache_type, cache_op, cache_result; cache_type = (config >> 0) & 0xff; if (cache_type > PERF_COUNT_HW_CACHE_MAX) return "unknown-ext-hardware-cache-type"; cache_op = (config >> 8) & 0xff; if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX) return "unknown-ext-hardware-cache-op"; cache_result = (config >> 16) & 0xff; if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) return "unknown-ext-hardware-cache-result"; if (!is_cache_op_valid(cache_type, cache_op)) return "invalid-cache"; return event_cache_name(cache_type, cache_op, cache_result); } case PERF_TYPE_SOFTWARE: if (config < PERF_COUNT_SW_MAX) return sw_event_names[config]; return "unknown-software"; case PERF_TYPE_TRACEPOINT: return tracepoint_id_to_name(config); default: break; } return "unknown"; } static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size) { int i, j; int n, longest = -1; for (i = 0; i < size; i++) { for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { n = strlen(names[i][j]); if (n > longest && !strncasecmp(*str, names[i][j], n)) longest = n; } if (longest > 0) { *str += longest; return i; } } return -1; } static enum event_result parse_generic_hw_event(const char **str, struct perf_event_attr *attr) { const char *s = *str; int cache_type = -1, cache_op = -1, cache_result = -1; cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX); /* * No fallback - if we cannot get a clear cache type * then bail out: */ if (cache_type == -1) return EVT_FAILED; while ((cache_op == -1 || cache_result == -1) && *s == '-') { ++s; if (cache_op == -1) { cache_op = parse_aliases(&s, hw_cache_op, PERF_COUNT_HW_CACHE_OP_MAX); if (cache_op >= 0) { if (!is_cache_op_valid(cache_type, cache_op)) return 0; continue; } } if (cache_result == -1) { cache_result = parse_aliases(&s, hw_cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX); if (cache_result >= 0) continue; } /* * Can't parse this as a cache op or result, so back up * to the '-'. */ --s; break; } /* * Fall back to reads: */ if (cache_op == -1) cache_op = PERF_COUNT_HW_CACHE_OP_READ; /* * Fall back to accesses: */ if (cache_result == -1) cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; attr->config = cache_type | (cache_op << 8) | (cache_result << 16); attr->type = PERF_TYPE_HW_CACHE; *str = s; return EVT_HANDLED; } static enum event_result parse_single_tracepoint_event(char *sys_name, const char *evt_name, unsigned int evt_length, char *flags, struct perf_event_attr *attr, const char **strp) { char evt_path[MAXPATHLEN]; char id_buf[4]; u64 id; int fd; if (flags) { if (!strncmp(flags, "record", strlen(flags))) { attr->sample_type |= PERF_SAMPLE_RAW; attr->sample_type |= PERF_SAMPLE_TIME; attr->sample_type |= PERF_SAMPLE_CPU; } } snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_name, evt_name); fd = open(evt_path, O_RDONLY); if (fd < 0) return EVT_FAILED; if (read(fd, id_buf, sizeof(id_buf)) < 0) { close(fd); return EVT_FAILED; } close(fd); id = atoll(id_buf); attr->config = id; attr->type = PERF_TYPE_TRACEPOINT; *strp = evt_name + evt_length; return EVT_HANDLED; } /* sys + ':' + event + ':' + flags*/ #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) static enum event_result parse_subsystem_tracepoint_event(char *sys_name, char *flags) { char evt_path[MAXPATHLEN]; struct dirent *evt_ent; DIR *evt_dir; snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name); evt_dir = opendir(evt_path); if (!evt_dir) { perror("Can't open event dir"); return EVT_FAILED; } while ((evt_ent = readdir(evt_dir))) { char event_opt[MAX_EVOPT_LEN + 1]; int len; unsigned int rem = MAX_EVOPT_LEN; if (!strcmp(evt_ent->d_name, ".") || !strcmp(evt_ent->d_name, "..") || !strcmp(evt_ent->d_name, "enable") || !strcmp(evt_ent->d_name, "filter")) continue; len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, evt_ent->d_name); if (len < 0) return EVT_FAILED; rem -= len; if (flags) { if (rem < strlen(flags) + 1) return EVT_FAILED; strcat(event_opt, ":"); strcat(event_opt, flags); } if (parse_events(NULL, event_opt, 0)) return EVT_FAILED; } return EVT_HANDLED_ALL; } static enum event_result parse_tracepoint_event(const char **strp, struct perf_event_attr *attr) { const char *evt_name; char *flags; char sys_name[MAX_EVENT_LENGTH]; unsigned int sys_length, evt_length; if (valid_debugfs_mount(debugfs_path)) return 0; evt_name = strchr(*strp, ':'); if (!evt_name) return EVT_FAILED; sys_length = evt_name - *strp; if (sys_length >= MAX_EVENT_LENGTH) return 0; strncpy(sys_name, *strp, sys_length); sys_name[sys_length] = '\0'; evt_name = evt_name + 1; flags = strchr(evt_name, ':'); if (flags) { /* split it out: */ evt_name = strndup(evt_name, flags - evt_name); flags++; } evt_length = strlen(evt_name); if (evt_length >= MAX_EVENT_LENGTH) return EVT_FAILED; if (!strcmp(evt_name, "*")) { *strp = evt_name + evt_length; return parse_subsystem_tracepoint_event(sys_name, flags); } else return parse_single_tracepoint_event(sys_name, evt_name, evt_length, flags, attr, strp); } static int check_events(const char *str, unsigned int i) { int n; n = strlen(event_symbols[i].symbol); if (!strncmp(str, event_symbols[i].symbol, n)) return n; n = strlen(event_symbols[i].alias); if (n) if (!strncmp(str, event_symbols[i].alias, n)) return n; return 0; } static enum event_result parse_symbolic_event(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; unsigned int i; int n; for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { n = check_events(str, i); if (n > 0) { attr->type = event_symbols[i].type; attr->config = event_symbols[i].config; *strp = str + n; return EVT_HANDLED; } } return EVT_FAILED; } static enum event_result parse_raw_event(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; u64 config; int n; if (*str != 'r') return EVT_FAILED; n = hex2u64(str + 1, &config); if (n > 0) { *strp = str + n + 1; attr->type = PERF_TYPE_RAW; attr->config = config; return EVT_HANDLED; } return EVT_FAILED; } static enum event_result parse_numeric_event(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; char *endp; unsigned long type; u64 config; type = strtoul(str, &endp, 0); if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { str = endp + 1; config = strtoul(str, &endp, 0); if (endp > str) { attr->type = type; attr->config = config; *strp = endp; return EVT_HANDLED; } } return EVT_FAILED; } static enum event_result parse_event_modifier(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; int eu = 1, ek = 1, eh = 1; if (*str++ != ':') return 0; while (*str) { if (*str == 'u') eu = 0; else if (*str == 'k') ek = 0; else if (*str == 'h') eh = 0; else break; ++str; } if (str >= *strp + 2) { *strp = str; attr->exclude_user = eu; attr->exclude_kernel = ek; attr->exclude_hv = eh; return 1; } return 0; } /* * Each event can have multiple symbolic names. * Symbolic names are (almost) exactly matched. */ static enum event_result parse_event_symbols(const char **str, struct perf_event_attr *attr) { enum event_result ret; ret = parse_tracepoint_event(str, attr); if (ret != EVT_FAILED) goto modifier; ret = parse_raw_event(str, attr); if (ret != EVT_FAILED) goto modifier; ret = parse_numeric_event(str, attr); if (ret != EVT_FAILED) goto modifier; ret = parse_symbolic_event(str, attr); if (ret != EVT_FAILED) goto modifier; ret = parse_generic_hw_event(str, attr); if (ret != EVT_FAILED) goto modifier; return EVT_FAILED; modifier: parse_event_modifier(str, attr); return ret; } static void store_event_type(const char *orgname) { char filename[PATH_MAX], *c; FILE *file; int id; sprintf(filename, "/sys/kernel/debug/tracing/events/%s/id", orgname); c = strchr(filename, ':'); if (c) *c = '/'; file = fopen(filename, "r"); if (!file) return; if (fscanf(file, "%i", &id) < 1) die("cannot store event ID"); fclose(file); perf_header__push_event(id, orgname); } int parse_events(const struct option *opt __used, const char *str, int unset __used) { struct perf_event_attr attr; enum event_result ret; if (strchr(str, ':')) store_event_type(str); for (;;) { if (nr_counters == MAX_COUNTERS) return -1; memset(&attr, 0, sizeof(attr)); ret = parse_event_symbols(&str, &attr); if (ret == EVT_FAILED) return -1; if (!(*str == 0 || *str == ',' || isspace(*str))) return -1; if (ret != EVT_HANDLED_ALL) { attrs[nr_counters] = attr; nr_counters++; } if (*str == 0) break; if (*str == ',') ++str; while (isspace(*str)) ++str; } return 0; } static const char * const event_type_descriptors[] = { "", "Hardware event", "Software event", "Tracepoint event", "Hardware cache event", }; /* * Print the events from <debugfs_mount_point>/tracing/events */ static void print_tracepoint_events(void) { DIR *sys_dir, *evt_dir; struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; if (valid_debugfs_mount(debugfs_path)) return; sys_dir = opendir(debugfs_path); if (!sys_dir) return; for_each_subsystem(sys_dir, sys_dirent, sys_next) { snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_dirent.d_name); evt_dir = opendir(dir_path); if (!evt_dir) continue; for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { snprintf(evt_path, MAXPATHLEN, "%s:%s", sys_dirent.d_name, evt_dirent.d_name); fprintf(stderr, " %-42s [%s]\n", evt_path, event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); } closedir(evt_dir); } closedir(sys_dir); } /* * Print the help text for the event symbols: */ void print_events(void) { struct event_symbol *syms = event_symbols; unsigned int i, type, op, prev_type = -1; char name[40]; fprintf(stderr, "\n"); fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { type = syms->type + 1; if (type >= ARRAY_SIZE(event_type_descriptors)) type = 0; if (type != prev_type) fprintf(stderr, "\n"); if (strlen(syms->alias)) sprintf(name, "%s OR %s", syms->symbol, syms->alias); else strcpy(name, syms->symbol); fprintf(stderr, " %-42s [%s]\n", name, event_type_descriptors[type]); prev_type = type; } fprintf(stderr, "\n"); for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { /* skip invalid cache type */ if (!is_cache_op_valid(type, op)) continue; for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { fprintf(stderr, " %-42s [%s]\n", event_cache_name(type, op, i), event_type_descriptors[4]); } } } fprintf(stderr, "\n"); fprintf(stderr, " %-42s [raw hardware event descriptor]\n", "rNNN"); fprintf(stderr, "\n"); print_tracepoint_events(); exit(129); }
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
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
You can’t perform that action at this time.