set test "labels"
if {![installtest_p]} {untested $test; return}
if {![utrace_p]} { untested $test; return }

# Compile a C program to use as the user-space probing target
set label_srcpath "[pwd]/labels.c"
set label_exepath "[pwd]/labels.x"
set label_sopath "[pwd]/labels.so"
set label_flags "additional_flags=-g"
set fp [open $label_srcpath "w"]
puts $fp "
int
foo ()
{
init_an_int:
  return 1;
}
int
main ()
{
  sleep(5);
  foo();
  int a = 0;
  int b = 0;
  char *c;
init_an_int:
  a = 2;
init_an_int_again:
  b = 3;
  c = \"abc\";
ptr_inited:
  return 1;
}
"
close $fp

set label_stppath "[pwd]/labels.stp"
set fp [open $label_stppath "w"]
puts $fp "
probe process(\"labels.x\").function(\"main@labels.c\").label(\"init_*\") {printf (\"VARS %s\\n\",\$\$vars)}
probe process(\"labels.x\").function(\"main@labels.c\").label(\"ptr_inited\") {printf (\"VARS %s\\n\",\$\$vars)}
probe process(\"labels.x\").function(\"main@labels.c\").label(\"init_an_int\") {printf (\"init_an_int\\n\")}
"
close $fp

set ok 0

set res [target_compile $label_srcpath $label_exepath executable $label_flags]
if { $res != "" } {
    verbose "target_compile failed: $res" 2
    fail "compiling labels.c $label_flags"
    catch {exec rm -f $label_srcpath $label_stppath}
    return
} else {
    pass "compiling labels.c $label_flags"
}

# line number error (expect no output at all, since no label found)

set ok 0
spawn stap --use-server-on-error=no -l "process(\"$label_exepath\").function(\"foo@${label_srcpath}:10\").label(\"*\")"

expect {
    -timeout 180
    -re {[^\r\n]+} { incr ok; exp_continue }
    timeout { fail "$test (timeout)" }
    eof { }
}
catch { close }; catch { wait }

if {$ok == 0} { pass "$test :N .label $ok" } { fail "$test :N .label $ok" }

# line number 

set ok 0
spawn stap -l "process(\"$label_exepath\").function(\"foo@${label_srcpath}:4\").label(\"*\")"

expect {
    -timeout 180
    -re {process.*function.*labels.c:5...label..init_an_int} { incr ok; exp_continue }
    timeout { fail "$test (timeout)" }
    eof { }
}
catch { close }; catch { wait }

if {$ok == 1} { pass "$test :N .label $ok" } { fail "$test :N .label $ok" }

# list of labels

spawn stap -l "process(\"$label_exepath\").function(\"*\").label(\"*\")"

set ok 0
expect {
    -timeout 180
    -re {^process[^\r\n]*function[^\r\n]*labels.c:5...label..init_an_int..\r\n}
	{ incr ok; exp_continue }
    -re {^process[^\r\n]*function[^\r\n]*labels.c:16...label..init_an_int..\r\n}
	{ incr ok; exp_continue }
    -re {^process[^\r\n]*function[^\r\n]*labels.c:18...label..init_an_int_again..\r\n}
	{ incr ok; exp_continue }
    -re {^process[^\r\n]*function[^\r\n]*labels.c:21...label..ptr_inited..\r\n}
	{ incr ok; exp_continue }
    timeout { fail "$test (timeout)" }
    eof { }
}
catch { close }; catch { wait }

if {$ok == 4} { pass "$test -l .label" } { fail "$test -l .label $ok" }

# label in an executable (only if we have uprobes)
foreach runtime [get_runtime_list] {
    set test_name "$test exe .label"
    if {$runtime != ""} {
	lappend test_name $runtime
	verbose -log "spawn stap -c $label_exepath $label_stppath --runtime=$runtime"
	spawn stap -c $label_exepath $label_stppath --runtime=$runtime
    } elseif {[uprobes_p]} {
	verbose -log "spawn stap -c $label_exepath $label_stppath"
	spawn stap -c $label_exepath $label_stppath
    } else {
	untested "$test_name"
	continue
    }

    # Note that the output can come out in a bit different order
    # depending on when the probes fire.  This is reasonable since
    # we've got 2 probes at the same probe point (.label("init_*") and
    # .label("init_an_int"))
    set ok 0
    expect {
	-timeout 180
	-re {VARS a=0x0 b=0x0.*init_an_int.*VARS a=0x2 b=0x0.*VARS a=0x2 b=0x3 c=0x[a-f01-9]} { incr ok; exp_continue }
	-re {init_an_int.*VARS a=0x0 b=0x0.*VARS a=0x2 b=0x0.*VARS a=0x2 b=0x3 c=0x[a-f01-9]} { incr ok; exp_continue }
	timeout { fail "$test (timeout)" }
	eof { }
    }
    catch { close }; catch { wait }
    if {$ok == 1} { pass "$test_name" } { fail "$test_name $ok" }
}

# address of label in an executable

set label_shpath "[pwd]/label.sh"
set fp [open $label_shpath "w"]
puts $fp "
	readelf --debug-dump $label_exepath | awk \"
	/init_an_int_again/ {have_label=1} 
	/DW_AT_low_pc/ {if (have_label) {print \$3;exit;}}
	\"
	"
close $fp
spawn sh $label_shpath
expect {
    -re {0x[0-9a-f]*} 
}
catch { close }; catch { wait }

set nomatch 0
spawn stap -p2 -l "process\(\"$label_exepath\"\).statement($expect_out(0,string))"
expect {
    -timeout 180
    -re {semantic error: no match} { incr nomatch; exp_continue }
    timeout { fail "$test (timeout)" }
    eof { }
}
catch { close }; catch { wait }

if {$nomatch == 0} { pass "$test exe .statement" } { fail "$test exe .statement" }

set ok 0

set label_flags "additional_flags=-g additional_flags=-shared additional_flags=-fPIC"
set res [target_compile $label_srcpath $label_sopath executable $label_flags]
if { $res != "" } {
    verbose "target_compile failed: $res" 2
    fail "compiling labels.c $label_flags"
    catch {exec rm -f $label_srcpath $label_stppath}
    return
} else {
    pass "compiling labels.c $label_flags"
}

# label in a shared object

spawn stap -p2 -l "process\(\"$label_sopath\"\).function\(\"\*\"\).label\(\"init_an_int_again\"\)"
expect {
    -timeout 180
    -re {process.*function} { incr ok; exp_continue }
    timeout { fail "$test (timeout)" }
    eof { }
}
catch { close }; catch { wait }

if {$ok == 1} { pass "$test so .label" } { fail "$test so .label $ok" }

# address of label in a shared object

set label_shpath "[pwd]/label.sh"
set fp [open $label_shpath "w"]
puts $fp "
	readelf --debug-dump $label_sopath | awk \"
	/init_an_int_again/ {have_label=1} 
	/DW_AT_low_pc/ {if (have_label) {print \$3;exit;}}
	\"
	"
close $fp
spawn sh $label_shpath
expect {
    -re {0x[0-9a-f]*} 
}
catch { close }; catch { wait }

set nomatch 0
spawn stap -p2 -l "process\(\"$label_sopath\"\).statement($expect_out(0,string))"
expect {
    -timeout 180
    -re {semantic error: no match} { incr nomatch; exp_continue }
    timeout { fail "$test (timeout)" }
    eof { }
}
catch { close }; catch { wait }

if {$nomatch == 0} { pass "$test so .statement" } { fail "$test so .statement" }

catch {exec rm -f $label_srcpath $label_stppath $label_exepath $label_shpath $label_sopath}
