-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding BTF tests that work over included BTF binary data, which is staticaly generated. The reason is to have the stable BTF data to build test code on. The data is generation is explained in the comment in tests/btf_data.h file. Adding 2 tests: TEST(btf, resolve_struct) - test of BTF::resolve_struct function and checks on the proper fields output TEST(btf, has_struct) - test of the BPFtrace::has_struct interface
- Loading branch information
Showing
3 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
#include <cstdlib> | ||
#include <unistd.h> | ||
#include "gtest/gtest.h" | ||
#include "btf.h" | ||
#include "struct.h" | ||
#include "bpftrace.h" | ||
#include "mocks.h" | ||
|
||
namespace bpftrace { | ||
namespace test { | ||
namespace btf { | ||
|
||
#include "btf_data.h" | ||
|
||
TEST(btf, resolve_struct) | ||
{ | ||
BTF btf(data, data_len); | ||
|
||
ASSERT_EQ(btf.has_data(), true); | ||
|
||
std::map<std::string, Struct> structs; | ||
|
||
btf.resolve_struct("Foo3", structs); | ||
|
||
//btf.dump_struct("Foo2"); | ||
|
||
ASSERT_EQ(structs.size(), 3U); | ||
ASSERT_EQ(structs.count("Foo1"), 1U); | ||
ASSERT_EQ(structs.count("Foo2"), 1U); | ||
ASSERT_EQ(structs.count("Foo3"), 1U); | ||
|
||
EXPECT_EQ(structs["Foo1"].size, 16); | ||
ASSERT_EQ(structs["Foo1"].fields.size(), 3U); | ||
ASSERT_EQ(structs["Foo1"].fields.count("a"), 1U); | ||
ASSERT_EQ(structs["Foo1"].fields.count("b"), 1U); | ||
ASSERT_EQ(structs["Foo1"].fields.count("c"), 1U); | ||
|
||
EXPECT_EQ(structs["Foo1"].fields["a"].type.type, Type::integer); | ||
EXPECT_EQ(structs["Foo1"].fields["a"].type.size, 4U); | ||
EXPECT_EQ(structs["Foo1"].fields["a"].offset, 0); | ||
|
||
EXPECT_EQ(structs["Foo1"].fields["b"].type.type, Type::integer); | ||
EXPECT_EQ(structs["Foo1"].fields["b"].type.size, 1U); | ||
EXPECT_EQ(structs["Foo1"].fields["b"].offset, 4); | ||
|
||
EXPECT_EQ(structs["Foo1"].fields["c"].type.type, Type::integer); | ||
EXPECT_EQ(structs["Foo1"].fields["c"].type.size, 8U); | ||
EXPECT_EQ(structs["Foo1"].fields["c"].offset, 8); | ||
|
||
EXPECT_EQ(structs["Foo2"].size, 24); | ||
ASSERT_EQ(structs["Foo2"].fields.size(), 2U); | ||
ASSERT_EQ(structs["Foo2"].fields.count("a"), 1U); | ||
ASSERT_EQ(structs["Foo2"].fields.count("f"), 1U); | ||
|
||
EXPECT_EQ(structs["Foo2"].fields["a"].type.type, Type::integer); | ||
EXPECT_EQ(structs["Foo2"].fields["a"].type.size, 4U); | ||
EXPECT_EQ(structs["Foo2"].fields["a"].offset, 0); | ||
|
||
EXPECT_EQ(structs["Foo2"].fields["f"].type.type, Type::cast); | ||
EXPECT_EQ(structs["Foo2"].fields["f"].type.size, 16U); | ||
EXPECT_EQ(structs["Foo2"].fields["f"].offset, 8); | ||
|
||
EXPECT_EQ(structs["Foo3"].size, 16); | ||
ASSERT_EQ(structs["Foo3"].fields.size(), 2U); | ||
ASSERT_EQ(structs["Foo3"].fields.count("foo1"), 1U); | ||
ASSERT_EQ(structs["Foo3"].fields.count("foo2"), 1U); | ||
|
||
EXPECT_EQ(structs["Foo3"].fields["foo1"].type.type, Type::cast); | ||
EXPECT_EQ(structs["Foo3"].fields["foo1"].type.size, 8U); | ||
EXPECT_EQ(structs["Foo3"].fields["foo1"].type.is_pointer, true); | ||
EXPECT_EQ(structs["Foo3"].fields["foo1"].type.cast_type, "Foo1"); | ||
EXPECT_EQ(structs["Foo3"].fields["foo1"].offset, 0); | ||
|
||
EXPECT_EQ(structs["Foo3"].fields["foo2"].type.type, Type::cast); | ||
EXPECT_EQ(structs["Foo3"].fields["foo2"].type.size, 8U); | ||
EXPECT_EQ(structs["Foo3"].fields["foo2"].type.is_pointer, true); | ||
EXPECT_EQ(structs["Foo3"].fields["foo2"].type.cast_type, "Foo2"); | ||
EXPECT_EQ(structs["Foo3"].fields["foo2"].offset, 8); | ||
|
||
structs.clear(); | ||
|
||
btf.resolve_struct("spinlock", structs); | ||
//btf.dump_structs(structs); | ||
|
||
ASSERT_EQ(structs.size(), 5U); | ||
ASSERT_EQ(structs.count("qspinlock"), 1U); | ||
ASSERT_EQ(structs.count("raw_spinlock"), 1U); | ||
ASSERT_EQ(structs.count("spinlock"), 1U); | ||
ASSERT_EQ(structs.count("type_0"), 1U); | ||
ASSERT_EQ(structs.count("type_9"), 1U); | ||
|
||
EXPECT_EQ(structs["qspinlock"].size, 4); | ||
ASSERT_EQ(structs["qspinlock"].fields.size(), 3U); | ||
ASSERT_EQ(structs["qspinlock"].fields.count("locked"), 1U); | ||
ASSERT_EQ(structs["qspinlock"].fields.count("val"), 1U); | ||
ASSERT_EQ(structs["qspinlock"].fields.count("pending"), 1U); | ||
|
||
EXPECT_EQ(structs["qspinlock"].fields["locked"].type.type, Type::integer); | ||
EXPECT_EQ(structs["qspinlock"].fields["locked"].type.size, 1U); | ||
EXPECT_EQ(structs["qspinlock"].fields["locked"].offset, 0); | ||
|
||
EXPECT_EQ(structs["qspinlock"].fields["pending"].type.type, Type::integer); | ||
EXPECT_EQ(structs["qspinlock"].fields["pending"].type.size, 1U); | ||
EXPECT_EQ(structs["qspinlock"].fields["pending"].offset, 1); | ||
|
||
EXPECT_EQ(structs["qspinlock"].fields["val"].type.type, Type::cast); | ||
EXPECT_EQ(structs["qspinlock"].fields["val"].type.size, 4U); | ||
EXPECT_EQ(structs["qspinlock"].fields["val"].offset, 0); | ||
EXPECT_EQ(structs["qspinlock"].fields["val"].type.cast_type, "type_9"); | ||
|
||
EXPECT_EQ(structs["raw_spinlock"].size, 16); | ||
ASSERT_EQ(structs["raw_spinlock"].fields.size(), 2U); | ||
ASSERT_EQ(structs["raw_spinlock"].fields.count("raw_lock"), 1U); | ||
ASSERT_EQ(structs["raw_spinlock"].fields.count("owner"), 1U); | ||
|
||
EXPECT_EQ(structs["raw_spinlock"].fields["raw_lock"].type.type, Type::cast); | ||
EXPECT_EQ(structs["raw_spinlock"].fields["raw_lock"].type.size, 4U); | ||
EXPECT_EQ(structs["raw_spinlock"].fields["raw_lock"].offset, 0); | ||
EXPECT_EQ(structs["raw_spinlock"].fields["raw_lock"].type.cast_type, "qspinlock"); | ||
|
||
EXPECT_EQ(structs["raw_spinlock"].fields["owner"].type.type, Type::cast); | ||
EXPECT_EQ(structs["raw_spinlock"].fields["owner"].type.size, 8U); | ||
EXPECT_EQ(structs["raw_spinlock"].fields["owner"].offset, 8); | ||
EXPECT_EQ(structs["raw_spinlock"].fields["owner"].type.cast_type, "type_0"); | ||
EXPECT_EQ(structs["raw_spinlock"].fields["owner"].type.is_pointer, true); | ||
|
||
EXPECT_EQ(structs["spinlock"].size, 16); | ||
ASSERT_EQ(structs["spinlock"].fields.size(), 2U); | ||
ASSERT_EQ(structs["spinlock"].fields.count("padding"), 1U); | ||
ASSERT_EQ(structs["spinlock"].fields.count("rlock"), 1U); | ||
|
||
EXPECT_EQ(structs["spinlock"].fields["padding"].type.type, Type::string); | ||
EXPECT_EQ(structs["spinlock"].fields["padding"].type.size, 10U); | ||
EXPECT_EQ(structs["spinlock"].fields["padding"].offset, 0); | ||
|
||
EXPECT_EQ(structs["spinlock"].fields["rlock"].type.type, Type::cast); | ||
EXPECT_EQ(structs["spinlock"].fields["rlock"].type.size, 16U); | ||
EXPECT_EQ(structs["spinlock"].fields["rlock"].offset, 0); | ||
EXPECT_EQ(structs["spinlock"].fields["rlock"].type.cast_type, "raw_spinlock"); | ||
} | ||
|
||
TEST(btf, has_struct) | ||
{ | ||
char *path = strdup("/tmp/XXXXXX"); | ||
ASSERT_TRUE(path != NULL); | ||
|
||
int fd = mkstemp(path); | ||
ASSERT_TRUE(fd >= 0); | ||
|
||
EXPECT_EQ(write(fd, data, data_len), data_len); | ||
close(fd); | ||
|
||
ASSERT_EQ(setenv("BPFTRACE_BTF", path, true), 0); | ||
|
||
auto bpftrace = get_strict_mock_bpftrace(); | ||
|
||
ASSERT_EQ(bpftrace->has_struct("Foo1"), true); | ||
ASSERT_EQ(bpftrace->has_struct("Foo"), false); | ||
ASSERT_EQ(bpftrace->has_struct("Foo2"), true); | ||
ASSERT_EQ(bpftrace->has_struct("Foo2"), true); | ||
ASSERT_EQ(bpftrace->has_struct("qspinlock"), true); | ||
ASSERT_EQ(bpftrace->has_struct("raw_spinlock"), true); | ||
ASSERT_EQ(bpftrace->has_struct("spinlock"), true); | ||
|
||
std::remove(path); | ||
} | ||
|
||
} // namespace btf | ||
} // namespace test | ||
} // namespace bpftrace |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
#if 0 | ||
|
||
The data consists of following source file objects: | ||
|
||
struct Foo1 { | ||
int a; | ||
char b; | ||
long c; | ||
}; | ||
|
||
struct Foo2 { | ||
int a; | ||
struct Foo1 f; | ||
}; | ||
|
||
struct Foo3 { | ||
struct Foo1 *foo1; | ||
struct Foo2 *foo2; | ||
}; | ||
|
||
typedef struct { | ||
int counter; | ||
} atomic_t; | ||
|
||
typedef struct refcount_struct { | ||
atomic_t refs; | ||
} refcount_t; | ||
|
||
typedef struct qspinlock { | ||
union { | ||
atomic_t val; | ||
struct { | ||
unsigned char locked; | ||
unsigned char pending; | ||
}; | ||
}; | ||
} arch_spinlock_t; | ||
|
||
typedef struct raw_spinlock { | ||
arch_spinlock_t raw_lock; | ||
void *owner; | ||
} raw_spinlock_t; | ||
|
||
typedef struct spinlock { | ||
union { | ||
struct raw_spinlock rlock; | ||
unsigned char padding[10]; | ||
}; | ||
} spinlock_t; | ||
|
||
|
||
It is generated via following commands: | ||
|
||
$ cat btf.c | ||
struct Foo1 { | ||
int a; | ||
char b; | ||
long c; | ||
}; | ||
|
||
struct Foo2 { | ||
int a; | ||
struct Foo1 f; | ||
}; | ||
|
||
struct Foo3 { | ||
struct Foo1 *foo1; | ||
struct Foo2 *foo2; | ||
}; | ||
|
||
typedef struct { | ||
int counter; | ||
} atomic_t; | ||
|
||
typedef struct refcount_struct { | ||
atomic_t refs; | ||
} refcount_t; | ||
|
||
typedef struct qspinlock { | ||
union { | ||
atomic_t val; | ||
struct { | ||
unsigned char locked; | ||
unsigned char pending; | ||
}; | ||
}; | ||
} arch_spinlock_t; | ||
|
||
typedef struct raw_spinlock { | ||
arch_spinlock_t raw_lock; | ||
void *owner; | ||
} raw_spinlock_t; | ||
|
||
typedef struct spinlock { | ||
union { | ||
struct raw_spinlock rlock; | ||
unsigned char padding[10]; | ||
}; | ||
} spinlock_t; | ||
|
||
struct Foo3 krava; | ||
spinlock_t sl; | ||
refcount_t ref; | ||
|
||
int main(void) | ||
{ | ||
return 0; | ||
} | ||
|
||
$ gcc -o btf -g btf.c | ||
$ pahole -J btf | ||
$ objcopy --dump-section .BTF=data btf | ||
$ xxd -i data | ||
|
||
The gcc optimize some union/structs out and do not include | ||
their names in the data. We see type_9 instead of atomic_t | ||
and refcount_t is left out completely. | ||
|
||
#endif | ||
|
||
unsigned char data[] = { | ||
0x9f, 0xeb, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x24, 0x02, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, | ||
0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, | ||
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x2b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, | ||
0x2d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, | ||
0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, | ||
0x20, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | ||
0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, | ||
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, 0x00, | ||
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, | ||
0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, | ||
0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x43, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, | ||
0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x00, 0x00, 0x00, | ||
0xa4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, | ||
0xbf, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0b, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, | ||
0x4c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x7c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, | ||
0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, | ||
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, | ||
0x04, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, | ||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, | ||
0x10, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, | ||
0x10, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, | ||
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, | ||
0x10, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, | ||
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, | ||
0x0a, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | ||
0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, | ||
0x01, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x08, 0x17, 0x00, 0x00, 0x00, 0x00, 0x46, 0x6f, 0x6f, | ||
0x31, 0x00, 0x46, 0x6f, 0x6f, 0x32, 0x00, 0x46, 0x6f, 0x6f, 0x33, 0x00, | ||
0x61, 0x00, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x73, 0x70, 0x69, 0x6e, 0x6c, | ||
0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x00, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, | ||
0x5f, 0x74, 0x00, 0x62, 0x00, 0x63, 0x00, 0x63, 0x68, 0x61, 0x72, 0x00, | ||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x00, 0x66, 0x00, 0x66, 0x6f, | ||
0x6f, 0x31, 0x00, 0x66, 0x6f, 0x6f, 0x32, 0x00, 0x69, 0x6e, 0x74, 0x00, | ||
0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x20, | ||
0x69, 0x6e, 0x74, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x75, 0x6e, 0x73, | ||
0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x6f, 0x77, | ||
0x6e, 0x65, 0x72, 0x00, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x00, | ||
0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x71, 0x73, 0x70, 0x69, | ||
0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x72, 0x61, 0x77, 0x5f, 0x6c, 0x6f, | ||
0x63, 0x6b, 0x00, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x70, 0x69, 0x6e, 0x6c, | ||
0x6f, 0x63, 0x6b, 0x00, 0x72, 0x65, 0x66, 0x63, 0x6f, 0x75, 0x6e, 0x74, | ||
0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x00, 0x72, 0x65, 0x66, 0x63, | ||
0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x00, 0x72, 0x65, 0x66, 0x73, 0x00, | ||
0x72, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x73, 0x70, 0x69, 0x6e, 0x6c, 0x6f, | ||
0x63, 0x6b, 0x00, 0x73, 0x70, 0x69, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, | ||
0x74, 0x00, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x63, | ||
0x68, 0x61, 0x72, 0x00, 0x76, 0x61, 0x6c, 0x00 | ||
}; | ||
|
||
unsigned int data_len = 812; |