Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix checkStack() to perform the necessary checks. #194

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 98 additions & 105 deletions src/dyninst/dyninst_comp.C
Original file line number Diff line number Diff line change
Expand Up @@ -1426,123 +1426,116 @@ const char *fixUnderscores(const char *str)
return buf;
}

bool checkStack(BPatch_thread *appThread,
const frameInfo_t correct_frame_info[],
unsigned num_correct_names,
int test_num, const char *test_name)
// Check is for each call in expected stack, the same amount of call is in
// the collected stack. This prevents wrong recursion frames.
bool hasSameAmountOfCalls(vector<BPatch_frame> & stack,
vector<frameInfo_t> expected_stack)
sashanicolas marked this conversation as resolved.
Show resolved Hide resolved
{
unsigned i, j;
// count occurances of functions in expected_stack
unordered_map<string, unsigned> expected_count;
for(const frameInfo_t frame : expected_stack)
expected_count[frame.function_name]++;

const int name_max = 256;
bool failed = false;
// go through stack frames and decrement count of expected
for(auto& frame: stack)
{
BPatch_function *func = frame.findFunction();
string name = func ? func->getName() : "";
if(expected_count.find(name)==expected_count.end()) continue;
expected_count[name]--;
}

BPatch_Vector<BPatch_frame> stack;
appThread->getCallStack(stack);
// count if all are zero
for(const frameInfo_t frame : expected_stack)
if(expected_count[frame.function_name]!=0) return false;

if (1) {
dprintf("Stack in test %d (%s):\n", test_num, test_name);
for( unsigned i = 0; i < stack.size(); i++) {
char name[name_max];
BPatch_function *func = stack[i].findFunction();
if (func == NULL)
strcpy(name, "[UNKNOWN]");
else
func->getName(name, name_max);
dprintf(" %10p: %s, fp = %p, type %s\n",
stack[i].getPC(),
name,
stack[i].getFP(),
frameTypeString(stack[i].getFrameType()));
return true;
}

}
dprintf("End of stack dump.\n");
}
// Makes sure the expected stack is subsequence of collected stack
bool isSubsequenceStack(vector<BPatch_frame> & stack, unsigned size,
const frameInfo_t expected_stack[], unsigned expected_size)
{
if(size==0) return false;
if(expected_size==0) return true;

if (stack.size() < num_correct_names) {
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" Stack trace should contain more frames.\n");
failed = true;
}
BPatch_function *func = stack[size-1].findFunction();
string name = func ? func->getName() : "";

if(name==expected_stack[expected_size-1].function_name ||
(expected_stack[expected_size-1].type==BPatch_frameTrampoline &&
stack[size-1].getFrameType()==BPatch_frameTrampoline) ||
(expected_stack[expected_size-1].type==BPatch_frameSignal &&
stack[size-1].getFrameType()==BPatch_frameSignal))
{
return isSubsequenceStack(stack, size-1, expected_stack, expected_size-1);
}

return isSubsequenceStack(stack, size-1, expected_stack, expected_size);
}

bool checkStack(BPatch_thread *appThread,
const frameInfo_t correct_frame_info[],
unsigned num_correct_names,
int test_num, const char *test_name)
{
bool failed = false;

BPatch_Vector<BPatch_frame> stack;
appThread->getCallStack(stack);

// Print out stack to "-verbose" log
dprintf("Stack in test %d (%s):\n", test_num, test_name);
for( unsigned i = 0; i < stack.size(); i++) {
BPatch_function *func = stack[i].findFunction();
string name = func ? func->getName() : "[UNKNOWN]";
dprintf(" %10p: %s, fp = %p, type %s\n",
stack[i].getPC(),
name.c_str(),
stack[i].getFP(),
frameTypeString(stack[i].getFrameType()));

}
dprintf("End of stack dump.\n");

// check if stack is at least same size of correct_frame_info
if (stack.size() < num_correct_names) {
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" Stack trace should contain more frames.\n");
failed = true;
}

for (i = 0, j = 0; i < num_correct_names; i++, j++) {
#if !defined(i386_unknown_nt4_0_test)
if (stack.size() && j < stack.size()-1 && stack[j].getFP() == 0) {
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" A stack frame other than the lowest has a null FP.\n");
failed = true;
break;
}
// check if any if the frames, except the last one, has FP!=0
for (unsigned j = 0; j < num_correct_names; j++)
{
if (stack.size() && j < stack.size()-1 && stack[j].getFP() == 0) {
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" A stack frame other than the lowest has a null FP.\n");
failed = true;
break;
}
} // for
#endif

if (stack.size() >= j)
break;
if (correct_frame_info[i].valid) {
char name[name_max], name2[name_max];

BPatch_function *func = stack[j].findFunction();
if (func != NULL)
func->getName(name, name_max);

BPatch_function *func2 =
appThread->getProcess()->findFunctionByEntry(reinterpret_cast<Dyninst::Address>(stack[j].getPC()));
if (func2 != NULL)
func2->getName(name2, name_max);

if ((func == NULL && func2 != NULL) ||
(func != NULL && func2 == NULL)) {
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" frame->findFunction() disagrees with thread->findFunctionByEntry()\n");
logerror(" frame->findFunction() returns %s\n",
name);
logerror(" thread->findFunctionByEntry() return %s\n",
name2);
failed = true;
break;
} else if (func!=NULL && func2!=NULL && strcmp(name, name2)!=0) {
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" BPatch_frame::findFunction disagrees with BPatch_thread::findFunctionByEntry\n");
failed = true;
break;
}

if (correct_frame_info[i].type != stack[j].getFrameType()) {
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" Stack frame #%d has wrong type, is %s, should be %s\n", i+1, frameTypeString(stack[i].getFrameType()), frameTypeString(correct_frame_info[i].type));
logerror(" Stack frame 0x%lx, 0x%lx\n", stack[i].getPC(), stack[i].getFP() );
failed = true;
break;
}
// check if stack follows sequence in correct_frame_info
if(!isSubsequenceStack(stack, stack.size(), correct_frame_info, num_correct_names))
{
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" Collected stack does not contain expected stack.\n");
failed = true;
}

if (stack[j].getFrameType() == BPatch_frameSignal ||
stack[j].getFrameType() == BPatch_frameTrampoline) {
// No further checking for these types right now
} else {
if (func == NULL) {
logerror("**Failed** test %d (%s)\n",
test_num, test_name);
logerror(" Stack frame #%d refers to an unknown function, should refer to %s\n", j+1, correct_frame_info[i].function_name);
failed = true;
break;
} else { /* func != NULL */
if (!hasExtraUnderscores(correct_frame_info[i].function_name))
strncpy(name, fixUnderscores(name), name_max);

if (strcmp(name, correct_frame_info[i].function_name) != 0) {
if (correct_frame_info[i].optional) {
j--;
continue;
}
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" Stack frame #%d refers to function %s, should be %s\n", j+1, name, correct_frame_info[i].function_name);
failed = true;
break;
}
}
}
}
}
// check if each call in correct_frame_info has the same amount of calls in stack
vector<frameInfo_t> expected_stack(correct_frame_info, correct_frame_info+num_correct_names);
if(!hasSameAmountOfCalls(stack, expected_stack))
{
logerror("**Failed** test %d (%s)\n", test_num, test_name);
logerror(" Possibly duplicate (case of wrong recursion) in collected stack. ");
failed = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be good to also add a check that the PC and FP addresses in the computed stack are in strictly decreasing order? That could be done using std::is_sorted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PC does not follow strictly decreasing order, as we don't know where the functions will be placed in memory, neither the base address of different libraries. So kill() could have a higher address compared to any test_stack_mutatee() or test_stack_func*(), while it is usually the last frame in these tests. But the FP for sure.


return !failed;
return !failed;
}

/* End Test8 Specific */
Expand Down
2 changes: 0 additions & 2 deletions src/dyninst/dyninst_comp.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,6 @@ COMPLIB_DLL_EXPORT int instByteCnt(BPatch_addressSpace* as, const char* fname,
COMPLIB_DLL_EXPORT int pointerSize(BPatch_image *img);

typedef struct {
bool valid;
bool optional;
BPatch_frameType type;
const char *function_name;
} frameInfo_t;
Expand Down
17 changes: 7 additions & 10 deletions src/dyninst/test_stack_1.C
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,17 @@ extern "C" DLLEXPORT TestMutator *test_stack_1_factory() {
test_results_t test_stack_1_Mutator::executeTest() {
appProc->continueExecution();
static const frameInfo_t correct_frame_info[] = {
#if defined( os_linux_test ) && (defined( arch_x86_test ) || defined( arch_x86_64_test ))
{ true, true, BPatch_frameNormal, "_dl_sysinfo_int80" },
#endif
#if !defined(rs6000_ibm_aix4_1_test)
{ false, false, BPatch_frameNormal, NULL },
{ BPatch_frameNormal, "" },
#endif
#if !defined(i386_unknown_nt4_0_test)
{ true, false, BPatch_frameNormal, "stop_process_" },
{ BPatch_frameNormal, "stop_process_" },
#endif
{ true, false, BPatch_frameNormal, "test_stack_1_func3" },
{ true, false, BPatch_frameNormal, "test_stack_1_func2" },
{ true, false, BPatch_frameNormal, "test_stack_1_func1" },
{ true, false, BPatch_frameNormal, "test_stack_1_mutateeTest" },
{ true, false, BPatch_frameNormal, "main" },
{ BPatch_frameNormal, "test_stack_1_func3" },
{ BPatch_frameNormal, "test_stack_1_func2" },
{ BPatch_frameNormal, "test_stack_1_func1" },
{ BPatch_frameNormal, "test_stack_1_mutatee" },
{ BPatch_frameNormal, "main" },
};

if (waitUntilStopped(bpatch, appProc, 1, "getCallStack") < 0) {
Expand Down
23 changes: 10 additions & 13 deletions src/dyninst/test_stack_2.C
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,18 @@ test_results_t test_stack_2_Mutator::executeTest() {

static const frameInfo_t correct_frame_info[] = {

#if defined( os_linux_test ) && (defined( arch_x86_test ) || defined( arch_x86_64_test ))
{ true, true, BPatch_frameNormal, "_dl_sysinfo_int80" },
#endif
#if !defined(rs6000_ibm_aix4_1_test)
{ false, false, BPatch_frameNormal, NULL },
{ BPatch_frameNormal, "" },
#endif
{ true, false, BPatch_frameNormal, "stop_process_" },
{ true, false, BPatch_frameNormal, "test_stack_2_func4" },
{ true, false, BPatch_frameNormal, "sigalrm_handler" },
{ true, false, BPatch_frameSignal, NULL },
{ true, false, BPatch_frameNormal, "test_stack_2_func3" },
{ true, false, BPatch_frameNormal, "test_stack_2_func2" },
{ true, false, BPatch_frameNormal, "test_stack_2_func1" },
{ true, false, BPatch_frameNormal, "test_stack_2_mutateeTest" },
{ true, false, BPatch_frameNormal, "main" }
{ BPatch_frameNormal, "stop_process_" },
{ BPatch_frameNormal, "test_stack_2_func4" },
{ BPatch_frameNormal, "sigalrm_handler" },
{ BPatch_frameSignal, "" },
{ BPatch_frameNormal, "test_stack_2_func3" },
{ BPatch_frameNormal, "test_stack_2_func2" },
{ BPatch_frameNormal, "test_stack_2_func1" },
{ BPatch_frameNormal, "test_stack_2_mutatee" },
{ BPatch_frameNormal, "main" }
};

if (waitUntilStopped(bpatch, appProc, 2, "getCallStack in signal handler") < 0) {
Expand Down
23 changes: 9 additions & 14 deletions src/dyninst/test_stack_3.C
Original file line number Diff line number Diff line change
Expand Up @@ -69,35 +69,30 @@ test_results_t test_stack_3_Mutator::executeTest() {
appProc->continueExecution();
static const frameInfo_t correct_frame_info[] = {

#if defined( os_linux_test ) && (defined( arch_x86_test ) || defined( arch_x86_64_test ))
{ true, true, BPatch_frameNormal, "_dl_sysinfo_int80" },
#endif
#if defined( os_aix_test ) && defined( arch_power_test )
/* AIX uses kill(), but the PC of a process in a syscall can
not be correctly determined, and appears to be the address
to which the syscall function will return. */
#elif defined( os_windows_test ) && (defined( arch_x86 ) || defined( arch_x86_64_test ))
/* Windows/x86 does not use kill(), so its lowermost frame will be
something unidentifiable in a system DLL. */
{ false, false, BPatch_frameNormal, NULL },
#else
{ true, false, BPatch_frameNormal, "kill" },
{ BPatch_frameNormal, "kill" },
#endif
#if ! defined( os_windows_test )
/* Windows/x86's stop_process_() calls DebugBreak(); it's
apparently normal to lose this frame. */
{ true, false, BPatch_frameNormal, "stop_process_" },
{ BPatch_frameNormal, "stop_process_" },
#endif
{ true, false, BPatch_frameNormal, "test_stack_3_func3" },
{ true, false, BPatch_frameTrampoline, NULL },
{ BPatch_frameNormal, "test_stack_3_func3" },
{ BPatch_frameTrampoline, "" },
/* On AIX and x86 (and others), if our instrumentation fires
before frame construction or after frame destruction, it's
acceptable to not report the function (since, after all, it
doesn't have a frame on the stack. */
{ true, true, BPatch_frameNormal, "test_stack_3_func2" },
{ true, false, BPatch_frameNormal, "test_stack_3_func1" },
{ true, false, BPatch_frameNormal, "test_stack_3_mutateeTest" },
{ true, false, BPatch_frameNormal, "main" }
acceptable to not report the function "test_stack_3_func2"
(since, after all, it doesn't have a frame on the stack. */
{ BPatch_frameNormal, "test_stack_3_func1" },
{ BPatch_frameNormal, "test_stack_3_mutatee" },
{ BPatch_frameNormal, "main" }
};

/* Wait for the mutatee to stop in test_stack_3_func1(). */
Expand Down
25 changes: 10 additions & 15 deletions src/dyninst/test_stack_4.C
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,16 @@ test_results_t test_stack_4_Mutator::executeTest() {

BPatch::bpatch->setInstrStackFrames(true);
appProc->continueExecution();
static const frameInfo_t correct_frame_info[] = {

#if defined( os_linux_test ) && (defined( arch_x86_test ) || defined( arch_x86_64_test ))
{ true, true, BPatch_frameNormal, "_dl_sysinfo_int80" },
#endif
{ true, false, BPatch_frameNormal, "kill" },
{ true, false, BPatch_frameNormal, "test_stack_4_func4" },
{ true, false, BPatch_frameTrampoline, NULL },
{ true, false, BPatch_frameNormal, "test_stack_4_sigalrm_handler" },
{ true, false, BPatch_frameSignal, NULL },
{ true, true, BPatch_frameNormal, "test_stack_4_func3" },
{ true, true, BPatch_frameNormal, "test_stack_4_func2" },
{ true, false, BPatch_frameNormal, "test_stack_4_func1" },
{ true, false, BPatch_frameNormal, "test_stack_4_mutateeTest" },
{ true, false, BPatch_frameNormal, "main" }
static const frameInfo_t correct_frame_info[] =
{
{ BPatch_frameNormal, "kill" },
{ BPatch_frameNormal, "test_stack_4_func4" },
{ BPatch_frameTrampoline, ""},
{ BPatch_frameNormal, "test_stack_4_sigalrm_handler" },
{ BPatch_frameSignal, ""},
{ BPatch_frameNormal, "test_stack_4_func1" },
{ BPatch_frameNormal, "test_stack_4_mutatee" },
{ BPatch_frameNormal, "main" }
};

/* Wait for the mutatee to stop in test_stack_4_func1(). */
Expand Down