diff options
| author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2011-08-11 07:02:59 -0400 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-08-12 08:28:45 -0400 |
| commit | 221d061182b8ff5507d5768aeeecbc74f01c5dfa (patch) | |
| tree | 79cd224ca76fd520e089860a3a0482ed2b718b0d | |
| parent | 13e27d7686c457c625242fc2c20be30eef942408 (diff) | |
perf probe: Fix to search local variables in appropriate scope
Fix perf probe to search local variables in appropriate local inlined
function scope. For example, pre_schedule() has only 2 local variables,
as below;
$ perf probe -L pre_schedule
<pre_schedule@/home/mhiramat/ksrc/linux-2.6/kernel/sched.c:0>
0 static inline void pre_schedule(struct rq *rq, struct task_struct *prev)
{
2 if (prev->sched_class->pre_schedule)
3 prev->sched_class->pre_schedule(rq, prev);
}
However, current perf probe shows 4 local variables on pre_schedule(),
because it searches variables in the caller(schedule()) scope.
$ perf probe -V pre_schedule
Available variables at pre_schedule
@<schedule+445>
int cpu
long unsigned int* switch_count
struct rq* rq
struct task_struct* prev
This patch fixes this issue by searching variables in the local scope of
the instance of inlined function. Here is the result.
$ perf probe -V pre_schedule
Available variables at pre_schedule
@<schedule+445>
struct rq* rq
struct task_struct* prev
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110259.19900.85664.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
| -rw-r--r-- | tools/perf/util/dwarf-aux.c | 33 | ||||
| -rw-r--r-- | tools/perf/util/dwarf-aux.h | 4 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.c | 132 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.h | 2 |
4 files changed, 144 insertions, 27 deletions
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index d9b8ad098498..425703a58638 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c | |||
| @@ -96,6 +96,39 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, | |||
| 96 | return *lineno ?: -ENOENT; | 96 | return *lineno ?: -ENOENT; |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); | ||
| 100 | |||
| 101 | /** | ||
| 102 | * cu_walk_functions_at - Walk on function DIEs at given address | ||
| 103 | * @cu_die: A CU DIE | ||
| 104 | * @addr: An address | ||
| 105 | * @callback: A callback which called with found DIEs | ||
| 106 | * @data: A user data | ||
| 107 | * | ||
| 108 | * Walk on function DIEs at given @addr in @cu_die. Passed DIEs | ||
| 109 | * should be subprogram or inlined-subroutines. | ||
| 110 | */ | ||
| 111 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
| 112 | int (*callback)(Dwarf_Die *, void *), void *data) | ||
| 113 | { | ||
| 114 | Dwarf_Die die_mem; | ||
| 115 | Dwarf_Die *sc_die; | ||
| 116 | int ret = -ENOENT; | ||
| 117 | |||
| 118 | /* Inlined function could be recursive. Trace it until fail */ | ||
| 119 | for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); | ||
| 120 | sc_die != NULL; | ||
| 121 | sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, | ||
| 122 | &die_mem)) { | ||
| 123 | ret = callback(sc_die, data); | ||
| 124 | if (ret) | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | |||
| 128 | return ret; | ||
| 129 | |||
| 130 | } | ||
| 131 | |||
| 99 | /** | 132 | /** |
| 100 | * die_compare_name - Compare diename and tname | 133 | * die_compare_name - Compare diename and tname |
| 101 | * @dw_die: a DIE | 134 | * @dw_die: a DIE |
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index c8e491bc133f..6f46106fd1d5 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h | |||
| @@ -34,6 +34,10 @@ extern const char *cu_get_comp_dir(Dwarf_Die *cu_die); | |||
| 34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | 34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, |
| 35 | const char **fname, int *lineno); | 35 | const char **fname, int *lineno); |
| 36 | 36 | ||
| 37 | /* Walk on funcitons at given address */ | ||
| 38 | extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
| 39 | int (*callback)(Dwarf_Die *, void *), void *data); | ||
| 40 | |||
| 37 | /* Compare diename and tname */ | 41 | /* Compare diename and tname */ |
| 38 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); | 42 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); |
| 39 | 43 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d6d57682473a..5c83b7d3d8ef 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -612,8 +612,8 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
| 612 | return ret; | 612 | return ret; |
| 613 | } | 613 | } |
| 614 | 614 | ||
| 615 | /* Find a variable in a subprogram die */ | 615 | /* Find a variable in a scope DIE */ |
| 616 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 616 | static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) |
| 617 | { | 617 | { |
| 618 | Dwarf_Die vr_die, *scopes; | 618 | Dwarf_Die vr_die, *scopes; |
| 619 | char buf[32], *ptr; | 619 | char buf[32], *ptr; |
| @@ -655,11 +655,11 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 655 | pr_debug("Searching '%s' variable in context.\n", | 655 | pr_debug("Searching '%s' variable in context.\n", |
| 656 | pf->pvar->var); | 656 | pf->pvar->var); |
| 657 | /* Search child die for local variables and parameters. */ | 657 | /* Search child die for local variables and parameters. */ |
| 658 | if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die)) | 658 | if (die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) |
| 659 | ret = convert_variable(&vr_die, pf); | 659 | ret = convert_variable(&vr_die, pf); |
| 660 | else { | 660 | else { |
| 661 | /* Search upper class */ | 661 | /* Search upper class */ |
| 662 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | 662 | nscopes = dwarf_getscopes_die(sc_die, &scopes); |
| 663 | ret = -ENOENT; | 663 | ret = -ENOENT; |
| 664 | while (nscopes-- > 1) { | 664 | while (nscopes-- > 1) { |
| 665 | pr_debug("Searching variables in %s\n", | 665 | pr_debug("Searching variables in %s\n", |
| @@ -717,26 +717,30 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | |||
| 717 | return 0; | 717 | return 0; |
| 718 | } | 718 | } |
| 719 | 719 | ||
| 720 | /* Call probe_finder callback with real subprogram DIE */ | 720 | /* Call probe_finder callback with scope DIE */ |
| 721 | static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | 721 | static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) |
| 722 | { | 722 | { |
| 723 | Dwarf_Die die_mem; | ||
| 724 | Dwarf_Attribute fb_attr; | 723 | Dwarf_Attribute fb_attr; |
| 725 | size_t nops; | 724 | size_t nops; |
| 726 | int ret; | 725 | int ret; |
| 727 | 726 | ||
| 728 | /* If no real subprogram, find a real one */ | 727 | if (!sc_die) { |
| 729 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | 728 | pr_err("Caller must pass a scope DIE. Program error.\n"); |
| 730 | sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem); | 729 | return -EINVAL; |
| 731 | if (!sp_die) { | 730 | } |
| 731 | |||
| 732 | /* If not a real subprogram, find a real one */ | ||
| 733 | if (dwarf_tag(sc_die) != DW_TAG_subprogram) { | ||
| 734 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { | ||
| 732 | pr_warning("Failed to find probe point in any " | 735 | pr_warning("Failed to find probe point in any " |
| 733 | "functions.\n"); | 736 | "functions.\n"); |
| 734 | return -ENOENT; | 737 | return -ENOENT; |
| 735 | } | 738 | } |
| 736 | } | 739 | } else |
| 740 | memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); | ||
| 737 | 741 | ||
| 738 | /* Get the frame base attribute/ops */ | 742 | /* Get the frame base attribute/ops from subprogram */ |
| 739 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | 743 | dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); |
| 740 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | 744 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); |
| 741 | if (ret <= 0 || nops == 0) { | 745 | if (ret <= 0 || nops == 0) { |
| 742 | pf->fb_ops = NULL; | 746 | pf->fb_ops = NULL; |
| @@ -754,7 +758,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 754 | } | 758 | } |
| 755 | 759 | ||
| 756 | /* Call finder's callback handler */ | 760 | /* Call finder's callback handler */ |
| 757 | ret = pf->callback(sp_die, pf); | 761 | ret = pf->callback(sc_die, pf); |
| 758 | 762 | ||
| 759 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | 763 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ |
| 760 | pf->fb_ops = NULL; | 764 | pf->fb_ops = NULL; |
| @@ -762,17 +766,82 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 762 | return ret; | 766 | return ret; |
| 763 | } | 767 | } |
| 764 | 768 | ||
| 769 | struct find_scope_param { | ||
| 770 | const char *function; | ||
| 771 | const char *file; | ||
| 772 | int line; | ||
| 773 | int diff; | ||
| 774 | Dwarf_Die *die_mem; | ||
| 775 | bool found; | ||
| 776 | }; | ||
| 777 | |||
| 778 | static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) | ||
| 779 | { | ||
| 780 | struct find_scope_param *fsp = data; | ||
| 781 | const char *file; | ||
| 782 | int lno; | ||
| 783 | |||
| 784 | /* Skip if declared file name does not match */ | ||
| 785 | if (fsp->file) { | ||
| 786 | file = dwarf_decl_file(fn_die); | ||
| 787 | if (!file || strcmp(fsp->file, file) != 0) | ||
| 788 | return 0; | ||
| 789 | } | ||
| 790 | /* If the function name is given, that's what user expects */ | ||
| 791 | if (fsp->function) { | ||
| 792 | if (die_compare_name(fn_die, fsp->function)) { | ||
| 793 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
| 794 | fsp->found = true; | ||
| 795 | return 1; | ||
| 796 | } | ||
| 797 | } else { | ||
| 798 | /* With the line number, find the nearest declared DIE */ | ||
| 799 | dwarf_decl_line(fn_die, &lno); | ||
| 800 | if (lno < fsp->line && fsp->diff > fsp->line - lno) { | ||
| 801 | /* Keep a candidate and continue */ | ||
| 802 | fsp->diff = fsp->line - lno; | ||
| 803 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
| 804 | fsp->found = true; | ||
| 805 | } | ||
| 806 | } | ||
| 807 | return 0; | ||
| 808 | } | ||
| 809 | |||
| 810 | /* Find an appropriate scope fits to given conditions */ | ||
| 811 | static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) | ||
| 812 | { | ||
| 813 | struct find_scope_param fsp = { | ||
| 814 | .function = pf->pev->point.function, | ||
| 815 | .file = pf->fname, | ||
| 816 | .line = pf->lno, | ||
| 817 | .diff = INT_MAX, | ||
| 818 | .die_mem = die_mem, | ||
| 819 | .found = false, | ||
| 820 | }; | ||
| 821 | |||
| 822 | cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); | ||
| 823 | |||
| 824 | return fsp.found ? die_mem : NULL; | ||
| 825 | } | ||
| 826 | |||
| 765 | static int probe_point_line_walker(const char *fname, int lineno, | 827 | static int probe_point_line_walker(const char *fname, int lineno, |
| 766 | Dwarf_Addr addr, void *data) | 828 | Dwarf_Addr addr, void *data) |
| 767 | { | 829 | { |
| 768 | struct probe_finder *pf = data; | 830 | struct probe_finder *pf = data; |
| 831 | Dwarf_Die *sc_die, die_mem; | ||
| 769 | int ret; | 832 | int ret; |
| 770 | 833 | ||
| 771 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) | 834 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
| 772 | return 0; | 835 | return 0; |
| 773 | 836 | ||
| 774 | pf->addr = addr; | 837 | pf->addr = addr; |
| 775 | ret = call_probe_finder(NULL, pf); | 838 | sc_die = find_best_scope(pf, &die_mem); |
| 839 | if (!sc_die) { | ||
| 840 | pr_warning("Failed to find scope of probe point.\n"); | ||
| 841 | return -ENOENT; | ||
| 842 | } | ||
| 843 | |||
| 844 | ret = call_probe_finder(sc_die, pf); | ||
| 776 | 845 | ||
| 777 | /* Continue if no error, because the line will be in inline function */ | 846 | /* Continue if no error, because the line will be in inline function */ |
| 778 | return ret < 0 ? ret : 0; | 847 | return ret < 0 ? ret : 0; |
| @@ -826,6 +895,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
| 826 | Dwarf_Addr addr, void *data) | 895 | Dwarf_Addr addr, void *data) |
| 827 | { | 896 | { |
| 828 | struct probe_finder *pf = data; | 897 | struct probe_finder *pf = data; |
| 898 | Dwarf_Die *sc_die, die_mem; | ||
| 829 | int ret; | 899 | int ret; |
| 830 | 900 | ||
| 831 | if (!line_list__has_line(&pf->lcache, lineno) || | 901 | if (!line_list__has_line(&pf->lcache, lineno) || |
| @@ -835,7 +905,14 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
| 835 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | 905 | pr_debug("Probe line found: line:%d addr:0x%llx\n", |
| 836 | lineno, (unsigned long long)addr); | 906 | lineno, (unsigned long long)addr); |
| 837 | pf->addr = addr; | 907 | pf->addr = addr; |
| 838 | ret = call_probe_finder(NULL, pf); | 908 | pf->lno = lineno; |
| 909 | sc_die = find_best_scope(pf, &die_mem); | ||
| 910 | if (!sc_die) { | ||
| 911 | pr_warning("Failed to find scope of probe point.\n"); | ||
| 912 | return -ENOENT; | ||
| 913 | } | ||
| 914 | |||
| 915 | ret = call_probe_finder(sc_die, pf); | ||
| 839 | 916 | ||
| 840 | /* | 917 | /* |
| 841 | * Continue if no error, because the lazy pattern will match | 918 | * Continue if no error, because the lazy pattern will match |
| @@ -1059,7 +1136,7 @@ found: | |||
| 1059 | } | 1136 | } |
| 1060 | 1137 | ||
| 1061 | /* Add a found probe point into trace event list */ | 1138 | /* Add a found probe point into trace event list */ |
| 1062 | static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | 1139 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) |
| 1063 | { | 1140 | { |
| 1064 | struct trace_event_finder *tf = | 1141 | struct trace_event_finder *tf = |
| 1065 | container_of(pf, struct trace_event_finder, pf); | 1142 | container_of(pf, struct trace_event_finder, pf); |
| @@ -1074,8 +1151,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1074 | } | 1151 | } |
| 1075 | tev = &tf->tevs[tf->ntevs++]; | 1152 | tev = &tf->tevs[tf->ntevs++]; |
| 1076 | 1153 | ||
| 1077 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1154 | /* Trace point should be converted from subprogram DIE */ |
| 1078 | &tev->point); | 1155 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
| 1156 | pf->pev->point.retprobe, &tev->point); | ||
| 1079 | if (ret < 0) | 1157 | if (ret < 0) |
| 1080 | return ret; | 1158 | return ret; |
| 1081 | 1159 | ||
| @@ -1090,7 +1168,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1090 | for (i = 0; i < pf->pev->nargs; i++) { | 1168 | for (i = 0; i < pf->pev->nargs; i++) { |
| 1091 | pf->pvar = &pf->pev->args[i]; | 1169 | pf->pvar = &pf->pev->args[i]; |
| 1092 | pf->tvar = &tev->args[i]; | 1170 | pf->tvar = &tev->args[i]; |
| 1093 | ret = find_variable(sp_die, pf); | 1171 | /* Variable should be found from scope DIE */ |
| 1172 | ret = find_variable(sc_die, pf); | ||
| 1094 | if (ret != 0) | 1173 | if (ret != 0) |
| 1095 | return ret; | 1174 | return ret; |
| 1096 | } | 1175 | } |
| @@ -1158,7 +1237,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) | |||
| 1158 | } | 1237 | } |
| 1159 | 1238 | ||
| 1160 | /* Add a found vars into available variables list */ | 1239 | /* Add a found vars into available variables list */ |
| 1161 | static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | 1240 | static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) |
| 1162 | { | 1241 | { |
| 1163 | struct available_var_finder *af = | 1242 | struct available_var_finder *af = |
| 1164 | container_of(pf, struct available_var_finder, pf); | 1243 | container_of(pf, struct available_var_finder, pf); |
| @@ -1173,8 +1252,9 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1173 | } | 1252 | } |
| 1174 | vl = &af->vls[af->nvls++]; | 1253 | vl = &af->vls[af->nvls++]; |
| 1175 | 1254 | ||
| 1176 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1255 | /* Trace point should be converted from subprogram DIE */ |
| 1177 | &vl->point); | 1256 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
| 1257 | pf->pev->point.retprobe, &vl->point); | ||
| 1178 | if (ret < 0) | 1258 | if (ret < 0) |
| 1179 | return ret; | 1259 | return ret; |
| 1180 | 1260 | ||
| @@ -1186,14 +1266,14 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1186 | if (vl->vars == NULL) | 1266 | if (vl->vars == NULL) |
| 1187 | return -ENOMEM; | 1267 | return -ENOMEM; |
| 1188 | af->child = true; | 1268 | af->child = true; |
| 1189 | die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem); | 1269 | die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); |
| 1190 | 1270 | ||
| 1191 | /* Find external variables */ | 1271 | /* Find external variables */ |
| 1192 | if (!af->externs) | 1272 | if (!af->externs) |
| 1193 | goto out; | 1273 | goto out; |
| 1194 | /* Don't need to search child DIE for externs. */ | 1274 | /* Don't need to search child DIE for externs. */ |
| 1195 | af->child = false; | 1275 | af->child = false; |
| 1196 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | 1276 | nscopes = dwarf_getscopes_die(sc_die, &scopes); |
| 1197 | while (nscopes-- > 1) | 1277 | while (nscopes-- > 1) |
| 1198 | die_find_child(&scopes[nscopes], collect_variables_cb, | 1278 | die_find_child(&scopes[nscopes], collect_variables_cb, |
| 1199 | (void *)af, &die_mem); | 1279 | (void *)af, &die_mem); |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index c478b42a2473..1132c8f0ce89 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
| @@ -57,7 +57,7 @@ struct probe_finder { | |||
| 57 | struct perf_probe_event *pev; /* Target probe event */ | 57 | struct perf_probe_event *pev; /* Target probe event */ |
| 58 | 58 | ||
| 59 | /* Callback when a probe point is found */ | 59 | /* Callback when a probe point is found */ |
| 60 | int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf); | 60 | int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf); |
| 61 | 61 | ||
| 62 | /* For function searching */ | 62 | /* For function searching */ |
| 63 | int lno; /* Line number */ | 63 | int lno; /* Line number */ |
