Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
B
bpftrace
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
bpftrace
Commits
5c31a0a6
Commit
5c31a0a6
authored
Aug 05, 2018
by
Alastair Robertson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Codegen for C structs
parent
2f0c01df
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
903 additions
and
98 deletions
+903
-98
src/ast/ast.h
src/ast/ast.h
+3
-1
src/ast/codegen_llvm.cpp
src/ast/codegen_llvm.cpp
+118
-18
src/ast/irbuilderbpf.cpp
src/ast/irbuilderbpf.cpp
+6
-5
src/ast/irbuilderbpf.h
src/ast/irbuilderbpf.h
+1
-1
src/ast/printer.cpp
src/ast/printer.cpp
+4
-1
src/ast/semantic_analyser.cpp
src/ast/semantic_analyser.cpp
+42
-24
src/parser.yy
src/parser.yy
+2
-6
src/types.cpp
src/types.cpp
+2
-0
src/types.h
src/types.h
+3
-0
tests/codegen.cpp
tests/codegen.cpp
+632
-0
tests/semantic_analyser.cpp
tests/semantic_analyser.cpp
+90
-42
No files found.
src/ast/ast.h
View file @
5c31a0a6
...
...
@@ -110,8 +110,10 @@ public:
class
Cast
:
public
Expression
{
public:
Cast
(
const
std
::
string
&
type
,
Expression
*
expr
)
:
cast_type
(
type
),
expr
(
expr
)
{
}
Cast
(
const
std
::
string
&
type
,
bool
is_pointer
,
Expression
*
expr
)
:
cast_type
(
type
),
is_pointer
(
is_pointer
),
expr
(
expr
)
{
}
std
::
string
cast_type
;
bool
is_pointer
;
Expression
*
expr
;
void
accept
(
Visitor
&
v
)
override
;
...
...
src/ast/codegen_llvm.cpp
View file @
5c31a0a6
...
...
@@ -180,7 +180,7 @@ void CodegenLLVM::visit(Call &call)
static
int
printf_id
=
0
;
auto
args
=
std
::
get
<
1
>
(
bpftrace_
.
printf_args_
.
at
(
printf_id
));
for
(
SizedType
t
:
args
)
for
(
SizedType
&
t
:
args
)
{
llvm
::
Type
*
ty
=
b_
.
GetType
(
t
);
elements
.
push_back
(
ty
);
...
...
@@ -291,32 +291,112 @@ void CodegenLLVM::visit(Binop &binop)
void
CodegenLLVM
::
visit
(
Unop
&
unop
)
{
assert
(
unop
.
expr
->
type
.
type
==
Type
::
integer
);
unop
.
expr
->
accept
(
*
this
);
switch
(
unop
.
op
)
{
case
bpftrace
:
:
Parser
::
token
::
LNOT
:
expr_
=
b_
.
CreateNot
(
expr_
);
break
;
case
bpftrace
:
:
Parser
::
token
::
BNOT
:
expr_
=
b_
.
CreateNeg
(
expr_
);
break
;
case
bpftrace
:
:
Parser
::
token
::
MUL
:
{
AllocaInst
*
dst
=
b_
.
CreateAllocaBPF
(
unop
.
expr
->
type
,
"deref"
);
b_
.
CreateProbeRead
(
dst
,
unop
.
expr
->
type
.
size
,
expr_
);
expr_
=
b_
.
CreateLoad
(
dst
);
b_
.
CreateLifetimeEnd
(
dst
);
break
;
SizedType
&
type
=
unop
.
expr
->
type
;
if
(
type
.
type
==
Type
::
integer
)
{
switch
(
unop
.
op
)
{
case
bpftrace
:
:
Parser
::
token
::
LNOT
:
expr_
=
b_
.
CreateNot
(
expr_
);
break
;
case
bpftrace
:
:
Parser
::
token
::
BNOT
:
expr_
=
b_
.
CreateNeg
(
expr_
);
break
;
case
bpftrace
:
:
Parser
::
token
::
MUL
:
{
int
size
=
type
.
size
;
if
(
type
.
is_pointer
)
{
// When dereferencing a 32-bit integer, only read in 32-bits, etc.
size
=
type
.
pointee_size
;
}
AllocaInst
*
dst
=
b_
.
CreateAllocaBPF
(
SizedType
(
type
.
type
,
size
),
"deref"
);
b_
.
CreateProbeRead
(
dst
,
size
,
expr_
);
expr_
=
b_
.
CreateLoad
(
dst
);
b_
.
CreateLifetimeEnd
(
dst
);
break
;
}
default:
abort
();
}
default:
abort
();
}
else
if
(
type
.
type
==
Type
::
cast
)
{
// Do nothing
}
else
{
abort
();
}
}
void
CodegenLLVM
::
visit
(
FieldAccess
&
acc
)
{
// TODO
SizedType
&
type
=
acc
.
expr
->
type
;
assert
(
type
.
type
==
Type
::
cast
);
acc
.
expr
->
accept
(
*
this
);
auto
&
field
=
bpftrace_
.
structs_
[
type
.
cast_type
].
fields
[
acc
.
field
];
if
(
type
.
is_internal
)
{
// The struct we are reading from has already been pulled into
// BPF-memory, e.g. by being stored in a map.
// Just read from the correct offset of expr_
Value
*
src
=
b_
.
CreateGEP
(
expr_
,
{
b_
.
getInt64
(
0
),
b_
.
getInt64
(
field
.
offset
)});
if
(
field
.
type
.
type
==
Type
::
cast
)
{
// TODO This should be do-able without allocating more memory here
AllocaInst
*
dst
=
b_
.
CreateAllocaBPF
(
field
.
type
,
"internal_"
+
type
.
cast_type
+
"."
+
acc
.
field
);
b_
.
CreateMemCpy
(
dst
,
src
,
field
.
type
.
size
,
1
);
expr_
=
dst
;
// TODO clean up dst memory?
}
else
if
(
field
.
type
.
type
==
Type
::
string
)
{
expr_
=
src
;
}
else
{
expr_
=
b_
.
CreateLoad
(
b_
.
GetType
(
field
.
type
),
src
);
}
}
else
{
// The struct we are reading from has not been pulled into BPF-memory,
// so expr_ will contain an external pointer to the start of the struct
Value
*
src
=
b_
.
CreateAdd
(
expr_
,
b_
.
getInt64
(
field
.
offset
));
if
(
field
.
type
.
type
==
Type
::
cast
&&
!
field
.
type
.
is_pointer
)
{
// struct X
// {
// struct Y y;
// };
//
// We are trying to access an embedded struct, e.g. "x.y"
//
// Instead of copying the entire struct Y in, we'll just store it as a
// pointer internally and dereference later when necessary.
expr_
=
src
;
}
else
if
(
field
.
type
.
type
==
Type
::
string
)
{
AllocaInst
*
dst
=
b_
.
CreateAllocaBPF
(
field
.
type
,
type
.
cast_type
+
"."
+
acc
.
field
);
b_
.
CreateProbeRead
(
dst
,
field
.
type
.
size
,
src
);
expr_
=
dst
;
}
else
{
AllocaInst
*
dst
=
b_
.
CreateAllocaBPF
(
field
.
type
,
type
.
cast_type
+
"."
+
acc
.
field
);
b_
.
CreateProbeRead
(
dst
,
field
.
type
.
size
,
src
);
expr_
=
b_
.
CreateLoad
(
dst
);
b_
.
CreateLifetimeEnd
(
dst
);
}
}
}
void
CodegenLLVM
::
visit
(
Cast
&
cast
)
{
// TODO
cast
.
expr
->
accept
(
*
this
);
}
void
CodegenLLVM
::
visit
(
ExprStatement
&
expr
)
...
...
@@ -336,12 +416,32 @@ void CodegenLLVM::visit(AssignMapStatement &assignment)
Value
*
val
,
*
expr
;
expr
=
expr_
;
AllocaInst
*
key
=
getMapKey
(
map
);
if
(
assignment
.
expr
->
type
.
type
==
Type
::
string
)
if
(
map
.
type
.
type
==
Type
::
string
)
{
val
=
expr
;
}
else
if
(
map
.
type
.
type
==
Type
::
cast
)
{
if
(
assignment
.
expr
->
type
.
is_internal
)
{
val
=
expr
;
}
else
{
// expr currently contains a pointer to the struct
// We now want to read the entire struct in so we can save it
AllocaInst
*
dst
=
b_
.
CreateAllocaBPF
(
map
.
type
,
map
.
ident
+
"_val"
);
b_
.
CreateProbeRead
(
dst
,
map
.
type
.
size
,
expr
);
val
=
dst
;
}
}
else
{
if
(
map
.
type
.
type
==
Type
::
integer
)
{
// Integers are always stored as 64-bit in map values
expr
=
b_
.
CreateIntCast
(
expr
,
b_
.
getInt64Ty
(),
false
);
}
val
=
b_
.
CreateAllocaBPF
(
map
.
type
,
map
.
ident
+
"_val"
);
b_
.
CreateStore
(
expr
,
val
);
}
...
...
@@ -425,7 +525,7 @@ AllocaInst *CodegenLLVM::getMapKey(Map &map)
{
size
+=
expr
->
type
.
size
;
}
key
=
b_
.
CreateAlloca
MapKey
(
size
,
map
.
ident
+
"_key"
);
key
=
b_
.
CreateAlloca
BPF
(
size
,
map
.
ident
+
"_key"
);
int
offset
=
0
;
for
(
Expression
*
expr
:
*
map
.
vargs
)
{
...
...
@@ -455,7 +555,7 @@ AllocaInst *CodegenLLVM::getQuantizeMapKey(Map &map, Value *log2)
{
size
+=
expr
->
type
.
size
;
}
key
=
b_
.
CreateAlloca
MapKey
(
size
,
map
.
ident
+
"_key"
);
key
=
b_
.
CreateAlloca
BPF
(
size
,
map
.
ident
+
"_key"
);
int
offset
=
0
;
for
(
Expression
*
expr
:
*
map
.
vargs
)
{
...
...
src/ast/irbuilderbpf.cpp
View file @
5c31a0a6
...
...
@@ -48,7 +48,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, const std::str
return
CreateAllocaBPF
(
ty
,
name
);
}
AllocaInst
*
IRBuilderBPF
::
CreateAlloca
MapKey
(
int
bytes
,
const
std
::
string
&
name
)
AllocaInst
*
IRBuilderBPF
::
CreateAlloca
BPF
(
int
bytes
,
const
std
::
string
&
name
)
{
llvm
::
Type
*
ty
=
ArrayType
::
get
(
getInt8Ty
(),
bytes
);
return
CreateAllocaBPF
(
ty
,
name
);
...
...
@@ -57,7 +57,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name)
llvm
::
Type
*
IRBuilderBPF
::
GetType
(
const
SizedType
&
stype
)
{
llvm
::
Type
*
ty
;
if
(
stype
.
type
==
Type
::
string
||
stype
.
type
==
Type
::
cast
)
if
(
stype
.
type
==
Type
::
string
||
(
stype
.
type
==
Type
::
cast
&&
!
stype
.
is_pointer
)
)
{
ty
=
ArrayType
::
get
(
getInt8Ty
(),
stype
.
size
);
}
...
...
@@ -121,22 +121,23 @@ Value *IRBuilderBPF::CreateMapLookupElem(Map &map, AllocaInst *key)
CreateCondBr
(
condition
,
lookup_success_block
,
lookup_failure_block
);
SetInsertPoint
(
lookup_success_block
);
if
(
map
.
type
.
type
==
Type
::
string
)
if
(
map
.
type
.
type
==
Type
::
string
||
map
.
type
.
type
==
Type
::
cast
)
CreateMemCpy
(
value
,
call
,
map
.
type
.
size
,
1
);
else
CreateStore
(
CreateLoad
(
getInt64Ty
(),
call
),
value
);
CreateBr
(
lookup_merge_block
);
SetInsertPoint
(
lookup_failure_block
);
if
(
map
.
type
.
type
==
Type
::
string
)
if
(
map
.
type
.
type
==
Type
::
string
||
map
.
type
.
type
==
Type
::
cast
)
CreateMemSet
(
value
,
getInt8
(
0
),
map
.
type
.
size
,
1
);
else
CreateStore
(
getInt64
(
0
),
value
);
CreateBr
(
lookup_merge_block
);
SetInsertPoint
(
lookup_merge_block
);
if
(
map
.
type
.
type
==
Type
::
string
)
if
(
map
.
type
.
type
==
Type
::
string
||
map
.
type
.
type
==
Type
::
cast
)
return
value
;
return
CreateLoad
(
value
);
}
...
...
src/ast/irbuilderbpf.h
View file @
5c31a0a6
...
...
@@ -20,7 +20,7 @@ public:
AllocaInst
*
CreateAllocaBPF
(
llvm
::
Type
*
ty
,
const
std
::
string
&
name
=
""
);
AllocaInst
*
CreateAllocaBPF
(
const
SizedType
&
stype
,
const
std
::
string
&
name
=
""
);
AllocaInst
*
CreateAlloca
MapKey
(
int
bytes
,
const
std
::
string
&
name
=
""
);
AllocaInst
*
CreateAlloca
BPF
(
int
bytes
,
const
std
::
string
&
name
=
""
);
llvm
::
Type
*
GetType
(
const
SizedType
&
stype
);
CallInst
*
CreateBpfPseudoCall
(
int
mapfd
);
CallInst
*
CreateBpfPseudoCall
(
Map
&
map
);
...
...
src/ast/printer.cpp
View file @
5c31a0a6
...
...
@@ -101,7 +101,10 @@ void Printer::visit(FieldAccess &acc)
void
Printer
::
visit
(
Cast
&
cast
)
{
std
::
string
indent
(
depth_
,
' '
);
out_
<<
indent
<<
"("
<<
cast
.
cast_type
<<
")"
<<
std
::
endl
;
if
(
cast
.
is_pointer
)
out_
<<
indent
<<
"("
<<
cast
.
cast_type
<<
"*)"
<<
std
::
endl
;
else
out_
<<
indent
<<
"("
<<
cast
.
cast_type
<<
")"
<<
std
::
endl
;
++
depth_
;
cast
.
expr
->
accept
(
*
this
);
...
...
src/ast/semantic_analyser.cpp
View file @
5c31a0a6
...
...
@@ -231,22 +231,31 @@ void SemanticAnalyser::visit(Unop &unop)
{
unop
.
expr
->
accept
(
*
this
);
SizedType
&
type
=
unop
.
expr
->
type
;
if
(
is_final_pass
()
&&
unop
.
expr
->
type
.
type
!=
Type
::
integer
&&
unop
.
expr
->
type
.
type
!=
Type
::
cast
)
{
!
(
type
.
type
==
Type
::
integer
)
&&
!
(
type
.
type
==
Type
::
cast
&&
unop
.
op
==
Parser
::
token
::
MUL
)
)
{
err_
<<
"The "
<<
opstr
(
unop
)
<<
" operator can not be used on expressions of type '"
<<
unop
.
expr
->
type
<<
"'"
<<
std
::
endl
;
<<
type
<<
"'"
<<
std
::
endl
;
}
if
(
unop
.
op
==
Parser
::
token
::
MUL
&&
unop
.
expr
->
type
.
type
==
Type
::
cast
)
{
std
::
string
cast_type
=
unop
.
expr
->
type
.
cast_type
;
if
(
cast_type
.
back
()
==
'*'
)
{
cast_type
.
pop_back
();
unop
.
type
=
SizedType
(
Type
::
cast
,
8
,
cast_type
);
if
(
unop
.
op
==
Parser
::
token
::
MUL
)
{
if
(
type
.
type
==
Type
::
cast
)
{
if
(
type
.
is_pointer
)
{
if
(
bpftrace_
.
structs_
.
count
(
type
.
cast_type
)
==
0
)
{
err_
<<
"Unknown struct/union: '"
<<
type
.
cast_type
<<
"'"
<<
std
::
endl
;
return
;
}
int
cast_size
=
bpftrace_
.
structs_
[
type
.
cast_type
].
size
;
unop
.
type
=
SizedType
(
Type
::
cast
,
cast_size
,
type
.
cast_type
);
}
else
{
err_
<<
"Can not dereference struct/union of type '"
<<
type
.
cast_type
<<
"'. "
<<
"It is not a pointer."
<<
std
::
endl
;
}
}
else
{
err_
<<
"Can not dereference struct/union of type '"
<<
cast_type
<<
"'. "
<<
"It is not a pointer."
<<
std
::
endl
;
else
if
(
type
.
type
==
Type
::
integer
)
{
unop
.
type
=
SizedType
(
Type
::
integer
,
type
.
size
);
}
}
else
{
...
...
@@ -258,30 +267,35 @@ void SemanticAnalyser::visit(FieldAccess &acc)
{
acc
.
expr
->
accept
(
*
this
);
if
(
acc
.
expr
->
type
.
type
!=
Type
::
cast
)
{
SizedType
&
type
=
acc
.
expr
->
type
;
if
(
type
.
type
!=
Type
::
cast
)
{
if
(
is_final_pass
())
{
err_
<<
"Can not access field '"
<<
acc
.
field
<<
"' on expression of type '"
<<
acc
.
expr
->
type
<<
"' on expression of type '"
<<
type
<<
"'"
<<
std
::
endl
;
}
return
;
}
std
::
string
cast_type
=
acc
.
expr
->
type
.
cast_type
;
if
(
cast_type
.
back
()
==
'*'
)
{
if
(
type
.
is_pointer
)
{
err_
<<
"Can not access field '"
<<
acc
.
field
<<
"' on type '"
<<
cast_type
<<
"'. Try dereferencing it first, or using '->'"
<<
type
.
cast_type
<<
"'. Try dereferencing it first, or using '->'"
<<
std
::
endl
;
return
;
}
if
(
bpftrace_
.
structs_
.
count
(
type
.
cast_type
)
==
0
)
{
err_
<<
"Unknown struct/union: '"
<<
type
.
cast_type
<<
"'"
<<
std
::
endl
;
return
;
}
auto
fields
=
bpftrace_
.
structs_
[
cast_type
].
fields
;
auto
fields
=
bpftrace_
.
structs_
[
type
.
cast_type
].
fields
;
if
(
fields
.
count
(
acc
.
field
)
==
0
)
{
err_
<<
"Struct/union of type '"
<<
cast_type
<<
"' does not contain "
err_
<<
"Struct/union of type '"
<<
type
.
cast_type
<<
"' does not contain "
<<
"a field named '"
<<
acc
.
field
<<
"'"
<<
std
::
endl
;
}
else
{
acc
.
type
=
fields
[
acc
.
field
].
type
;
acc
.
type
.
is_internal
=
type
.
is_internal
;
}
}
...
...
@@ -289,22 +303,20 @@ void SemanticAnalyser::visit(Cast &cast)
{
cast
.
expr
->
accept
(
*
this
);
std
::
string
cast_type
=
cast
.
cast_type
;
if
(
cast_type
.
back
()
==
'*'
)
cast_type
.
pop_back
();
if
(
bpftrace_
.
structs_
.
count
(
cast_type
)
==
0
)
{
err_
<<
"Unknown struct/union: '"
<<
cast_type
<<
"'"
<<
std
::
endl
;
if
(
bpftrace_
.
structs_
.
count
(
cast
.
cast_type
)
==
0
)
{
err_
<<
"Unknown struct/union: '"
<<
cast
.
cast_type
<<
"'"
<<
std
::
endl
;
return
;
}
int
cast_size
;
if
(
cast
.
cast_type
.
back
()
==
'*'
)
{
if
(
cast
.
is_pointer
)
{
cast_size
=
sizeof
(
uintptr_t
);
}
else
{
cast_size
=
bpftrace_
.
structs_
[
cast
.
cast_type
].
size
;
}
cast
.
type
=
SizedType
(
Type
::
cast
,
cast_size
,
cast
.
cast_type
);
cast
.
type
.
is_pointer
=
cast
.
is_pointer
;
}
void
SemanticAnalyser
::
visit
(
ExprStatement
&
expr
)
...
...
@@ -338,6 +350,11 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
else
{
// This map hasn't been seen before
map_val_
.
insert
({
map_ident
,
assignment
.
expr
->
type
});
if
(
map_val_
[
map_ident
].
type
==
Type
::
integer
)
{
// Store all integer values as 64-bit in maps, so that there will
// be space for any integer to be assigned to the map later
map_val_
[
map_ident
].
size
=
8
;
}
}
if
(
assignment
.
expr
->
type
.
type
==
Type
::
cast
)
{
...
...
@@ -351,6 +368,7 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
}
else
{
map_val_
[
map_ident
].
cast_type
=
cast_type
;
map_val_
[
map_ident
].
is_internal
=
true
;
}
}
}
...
...
src/parser.yy
View file @
5c31a0a6
...
...
@@ -93,7 +93,6 @@ void yyerror(bpftrace::Driver &driver, const char *s);
%type <ast::AttachPointList *> attach_points
%type <ast::AttachPoint *> attach_point
%type <std::string> wildcard
%type <std::string> type
%type <std::string> ident
%right ASSIGN
...
...
@@ -190,11 +189,8 @@ expr : INT { $$ = new ast::Integer($1); }
| MUL expr %prec DEREF { $$ = new ast::Unop(token::MUL, $2); }
| expr DOT ident { $$ = new ast::FieldAccess($1, $3); }
| expr PTR ident { $$ = new ast::FieldAccess(new ast::Unop(token::MUL, $1), $3); }
| "(" type ")" expr %prec CAST { $$ = new ast::Cast($2, $4); }
;
type : IDENT { $$ = $1; }
| IDENT MUL { $$ = $1 + "*"; }
| "(" IDENT ")" expr %prec CAST { $$ = new ast::Cast($2, false, $4); }
| "(" IDENT MUL ")" expr %prec CAST { $$ = new ast::Cast($2, true, $5); }
;
ident : IDENT { $$ = $1; }
...
...
src/types.cpp
View file @
5c31a0a6
...
...
@@ -13,6 +13,8 @@ std::ostream &operator<<(std::ostream &os, Type type)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
SizedType
&
type
)
{
os
<<
type
.
type
;
if
(
type
.
is_pointer
)
os
<<
"*"
;
return
os
;
}
...
...
src/types.h
View file @
5c31a0a6
...
...
@@ -36,6 +36,9 @@ public:
Type
type
;
size_t
size
;
std
::
string
cast_type
;
bool
is_internal
=
false
;
bool
is_pointer
=
false
;
size_t
pointee_size
;
bool
operator
==
(
const
SizedType
&
t
)
const
;
};
...
...
tests/codegen.cpp
View file @
5c31a0a6
This diff is collapsed.
Click to expand it.
tests/semantic_analyser.cpp
View file @
5c31a0a6
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "bpftrace.h"
#include "clang_parser.h"
#include "driver.h"
#include "semantic_analyser.h"
...
...
@@ -19,6 +20,9 @@ void test(BPFtrace &bpftrace, Driver &driver, const std::string &input, int expe
{
ASSERT_EQ
(
driver
.
parse_str
(
input
),
0
);
ClangParser
clang
;
clang
.
parse
(
driver
.
root_
,
bpftrace
.
structs_
);
std
::
stringstream
out
;
ast
::
SemanticAnalyser
semantics
(
driver
.
root_
,
bpftrace
,
out
);
std
::
stringstream
msg
;
...
...
@@ -40,21 +44,7 @@ void test(Driver &driver, const std::string &input, int expected_result=0)
void
test
(
const
std
::
string
&
input
,
int
expected_result
=
0
)
{
Field
field
=
{
SizedType
(
Type
::
integer
,
8
),
0
};
Field
mystr
=
{
SizedType
(
Type
::
string
,
8
),
8
};
Field
type2_field_ptr
=
{
SizedType
(
Type
::
cast
,
8
,
"type2*"
),
16
};
Field
type2_field
=
{
SizedType
(
Type
::
cast
,
8
,
"type2"
),
24
};
Struct
type1
=
{
16
,
{{
"field"
,
field
},
{
"mystr"
,
mystr
},
{
"type2ptr"
,
type2_field_ptr
},
{
"type2"
,
type2_field
}}
};
Struct
type2
=
{
8
,
{{
"field"
,
field
}}
};
BPFtrace
bpftrace
;
bpftrace
.
structs_
[
"type1"
]
=
type1
;
bpftrace
.
structs_
[
"type2"
]
=
type2
;
Driver
driver
;
test
(
bpftrace
,
driver
,
input
,
expected_result
);
}
...
...
@@ -217,6 +207,34 @@ TEST(semantic_analyser, variable_type)
EXPECT_EQ
(
st
,
assignment
->
var
->
type
);
}
TEST
(
semantic_analyser
,
map_integer_sizes
)
{
Driver
driver
;
std
::
string
structs
=
"struct type1 { int x; }"
;
test
(
driver
,
structs
+
"kprobe:f { $x = ((type1)0).x; @x = $x; }"
,
0
);
auto
var_assignment
=
static_cast
<
ast
::
AssignVarStatement
*>
(
driver
.
root_
->
probes
->
at
(
0
)
->
stmts
->
at
(
0
));
auto
map_assignment
=
static_cast
<
ast
::
AssignMapStatement
*>
(
driver
.
root_
->
probes
->
at
(
0
)
->
stmts
->
at
(
1
));
EXPECT_EQ
(
SizedType
(
Type
::
integer
,
4
),
var_assignment
->
var
->
type
);
EXPECT_EQ
(
SizedType
(
Type
::
integer
,
8
),
map_assignment
->
map
->
type
);
}
TEST
(
semantic_analyser
,
unop_dereference
)
{
test
(
"kprobe:f { *0; }"
,
0
);
test
(
"struct X { int n; } kprobe:f { $x = (X*)0; *$x; }"
,
0
);
test
(
"struct X { int n; } kprobe:f { $x = (X)0; *$x; }"
,
1
);
test
(
"kprobe:f { *
\"
0
\"
; }"
,
10
);
}
TEST
(
semantic_analyser
,
unop_not
)
{
test
(
"kprobe:f { ~0; }"
,
0
);
test
(
"struct X { int n; } kprobe:f { $x = (X*)0; ~$x; }"
,
10
);
test
(
"struct X { int n; } kprobe:f { $x = (X)0; ~$x; }"
,
10
);
test
(
"kprobe:f { ~
\"
0
\"
; }"
,
10
);
}
TEST
(
semantic_analyser
,
printf
)
{
test
(
"kprobe:f { printf(
\"
hi
\"
) }"
,
0
);
...
...
@@ -370,24 +388,28 @@ TEST(semantic_analyser, profile)
TEST
(
semantic_analyser
,
variable_cast_types
)
{
test
(
"kprobe:f { $x = (type1)cpu; $x = (type1)cpu; }"
,
0
);
test
(
"kprobe:f { $x = (type1)cpu; $x = (type2)cpu; }"
,
1
);
std
::
string
structs
=
"struct type1 { int field; } struct type2 { int field; }"
;
test
(
structs
+
"kprobe:f { $x = (type1)cpu; $x = (type1)cpu; }"
,
0
);
test
(
structs
+
"kprobe:f { $x = (type1)cpu; $x = (type2)cpu; }"
,
1
);
}
TEST
(
semantic_analyser
,
map_cast_types
)
{
test
(
"kprobe:f { @x = (type1)cpu; @x = (type1)cpu; }"
,
0
);
test
(
"kprobe:f { @x = (type1)cpu; @x = (type2)cpu; }"
,
1
);
std
::
string
structs
=
"struct type1 { int field; } struct type2 { int field; }"
;
test
(
structs
+
"kprobe:f { @x = (type1)cpu; @x = (type1)cpu; }"
,
0
);
test
(
structs
+
"kprobe:f { @x = (type1)cpu; @x = (type2)cpu; }"
,
1
);
}
TEST
(
semantic_analyser
,
variable_casts_are_local
)
{
test
(
"kprobe:f { $x = (type1)cpu } kprobe:g { $x = (type2)cpu; }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; } struct type2 { int field; }"
;
test
(
structs
+
"kprobe:f { $x = (type1)cpu } kprobe:g { $x = (type2)cpu; }"
,
0
);
}
TEST
(
semantic_analyser
,
map_casts_are_global
)
{
test
(
"kprobe:f { @x = (type1)cpu } kprobe:g { @x = (type2)cpu; }"
,
1
);
std
::
string
structs
=
"struct type1 { int field; } struct type2 { int field; }"
;
test
(
structs
+
"kprobe:f { @x = (type1)cpu } kprobe:g { @x = (type2)cpu; }"
,
1
);
}
TEST
(
semantic_analyser
,
cast_unknown_type
)
...
...
@@ -397,50 +419,76 @@ TEST(semantic_analyser, cast_unknown_type)
TEST
(
semantic_analyser
,
field_access
)
{
test
(
"kprobe:f { ((type1)cpu).field }"
,
0
);
test
(
"kprobe:f { $x = (type1)cpu; $x.field }"
,
0
);
test
(
"kprobe:f { @x = (type1)cpu; @x.field }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; }"
;
test
(
structs
+
"kprobe:f { ((type1)cpu).field }"
,
0
);
test
(
structs
+
"kprobe:f { $x = (type1)cpu; $x.field }"
,
0
);
test
(
structs
+
"kprobe:f { @x = (type1)cpu; @x.field }"
,
0
);
}
TEST
(
semantic_analyser
,
field_access_wrong_field
)
{
test
(
"kprobe:f { ((type1)cpu).blah }"
,
1
);
test
(
"kprobe:f { $x = (type1)cpu; $x.blah }"
,
1
);
test
(
"kprobe:f { @x = (type1)cpu; @x.blah }"
,
1
);
std
::
string
structs
=
"struct type1 { int field; }"
;
test
(
structs
+
"kprobe:f { ((type1)cpu).blah }"
,
1
);
test
(
structs
+
"kprobe:f { $x = (type1)cpu; $x.blah }"
,
1
);
test
(
structs
+
"kprobe:f { @x = (type1)cpu; @x.blah }"
,
1
);
}
TEST
(
semantic_analyser
,
field_access_wrong_expr
)
{
test
(
"kprobe:f { 1234->field }"
,
10
);
std
::
string
structs
=
"struct type1 { int field; }"
;
test
(
structs
+
"kprobe:f { 1234->field }"
,
10
);
}
TEST
(
semantic_analyser
,
field_access_types
)
{
test
(
"kprobe:f { ((type1)0).field == 123 }"
,
0
);
test
(
"kprobe:f { ((type1)0).field ==
\"
abc
\"
}"
,
10
)
;
std
::
string
structs
=
"struct type1 { int field; char mystr[8]; }"
"struct type2 { int field; }"
;
test
(
"kprobe:f { ((type1)0).mystr ==
\"
abc
\"
}"
,
0
);
test
(
"kprobe:f { ((type1)0).mystr == 123
}"
,
10
);
test
(
structs
+
"kprobe:f { ((type1)0).field == 123
}"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)0).field ==
\"
abc
\"
}"
,
10
);
test
(
"kprobe:f { ((type1)0).field == ((type2)0).field }"
,
0
);
test
(
"kprobe:f { ((type1)0).mystr == ((type2)0).field }"
,
10
);
test
(
structs
+
"kprobe:f { ((type1)0).mystr ==
\"
abc
\"
}"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)0).mystr == 123 }"
,
10
);
test
(
structs
+
"kprobe:f { ((type1)0).field == ((type2)0).field }"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)0).mystr == ((type2)0).field }"
,
10
);
}
TEST
(
semantic_analyser
,
field_access_pointer
)
{
test
(
"kprobe:f { ((type1*)0)->field }"
,
0
);
test
(
"kprobe:f { ((type1*)0).field }"
,
1
);
test
(
"kprobe:f { *((type1*)0) }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; }"
;
test
(
structs
+
"kprobe:f { ((type1*)0)->field }"
,
0
);
test
(
structs
+
"kprobe:f { ((type1*)0).field }"
,
1
);
test
(
structs
+
"kprobe:f { *((type1*)0) }"
,
0
);
}
TEST
(
semantic_analyser
,
field_access_sub_struct
)
{
test
(
"kprobe:f { ((type1)0).type2ptr->field }"
,
0
);
test
(
"kprobe:f { ((type1)0).type2.field }"
,
0
);
test
(
"kprobe:f { $x = (type2)0; $x = ((type1)0).type2 }"
,
0
);
test
(
"kprobe:f { $x = (type2*)0; $x = ((type1)0).type2ptr }"
,
0
);
test
(
"kprobe:f { $x = (type1)0; $x = ((type1)0).type2 }"
,
1
);
test
(
"kprobe:f { $x = (type1*)0; $x = ((type1)0).type2ptr }"
,
1
);
std
::
string
structs
=
"struct type1 { struct type2 *type2ptr; struct type2 type2; }"
"struct type2 { int field; }"
;
test
(
structs
+
"kprobe:f { ((type1)0).type2ptr->field }"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)0).type2.field }"
,
0
);
test
(
structs
+
"kprobe:f { $x = (type2)0; $x = ((type1)0).type2 }"
,
0
);
test
(
structs
+
"kprobe:f { $x = (type2*)0; $x = ((type1)0).type2ptr }"
,
0
);
test
(
structs
+
"kprobe:f { $x = (type1)0; $x = ((type1)0).type2 }"
,
1
);
test
(
structs
+
"kprobe:f { $x = (type1*)0; $x = ((type1)0).type2ptr }"
,
1
);
}
TEST
(
semantic_analyser
,
field_access_is_internal
)
{
Driver
driver
;
std
::
string
structs
=
"struct type1 { int x; }"
;
test
(
driver
,
structs
+
"kprobe:f { $x = ((type1)0).x }"
,
0
);
auto
var_assignment1
=
static_cast
<
ast
::
AssignVarStatement
*>
(
driver
.
root_
->
probes
->
at
(
0
)
->
stmts
->
at
(
0
));
EXPECT_EQ
(
false
,
var_assignment1
->
var
->
type
.
is_internal
);
test
(
driver
,
structs
+
"kprobe:f { @type1 = (type1)0; $x = @type1.x }"
,
0
);
auto
map_assignment
=
static_cast
<
ast
::
AssignMapStatement
*>
(
driver
.
root_
->
probes
->
at
(
0
)
->
stmts
->
at
(
0
));
auto
var_assignment2
=
static_cast
<
ast
::
AssignVarStatement
*>
(
driver
.
root_
->
probes
->
at
(
0
)
->
stmts
->
at
(
1
));
EXPECT_EQ
(
true
,
map_assignment
->
map
->
type
.
is_internal
);
EXPECT_EQ
(
true
,
var_assignment2
->
var
->
type
.
is_internal
);
}
}
// namespace semantic_analyser
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment