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
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:
...
@@ -110,8 +110,10 @@ public:
class
Cast
:
public
Expression
{
class
Cast
:
public
Expression
{
public:
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
;
std
::
string
cast_type
;
bool
is_pointer
;
Expression
*
expr
;
Expression
*
expr
;
void
accept
(
Visitor
&
v
)
override
;
void
accept
(
Visitor
&
v
)
override
;
...
...
src/ast/codegen_llvm.cpp
View file @
5c31a0a6
...
@@ -180,7 +180,7 @@ void CodegenLLVM::visit(Call &call)
...
@@ -180,7 +180,7 @@ void CodegenLLVM::visit(Call &call)
static
int
printf_id
=
0
;
static
int
printf_id
=
0
;
auto
args
=
std
::
get
<
1
>
(
bpftrace_
.
printf_args_
.
at
(
printf_id
));
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
);
llvm
::
Type
*
ty
=
b_
.
GetType
(
t
);
elements
.
push_back
(
ty
);
elements
.
push_back
(
ty
);
...
@@ -291,32 +291,112 @@ void CodegenLLVM::visit(Binop &binop)
...
@@ -291,32 +291,112 @@ void CodegenLLVM::visit(Binop &binop)
void
CodegenLLVM
::
visit
(
Unop
&
unop
)
void
CodegenLLVM
::
visit
(
Unop
&
unop
)
{
{
assert
(
unop
.
expr
->
type
.
type
==
Type
::
integer
);
unop
.
expr
->
accept
(
*
this
);
unop
.
expr
->
accept
(
*
this
);
switch
(
unop
.
op
)
{
SizedType
&
type
=
unop
.
expr
->
type
;
case
bpftrace
:
:
Parser
::
token
::
LNOT
:
expr_
=
b_
.
CreateNot
(
expr_
);
break
;
if
(
type
.
type
==
Type
::
integer
)
case
bpftrace
:
:
Parser
::
token
::
BNOT
:
expr_
=
b_
.
CreateNeg
(
expr_
);
break
;
{
case
bpftrace
:
:
Parser
::
token
::
MUL
:
switch
(
unop
.
op
)
{
{
case
bpftrace
:
:
Parser
::
token
::
LNOT
:
expr_
=
b_
.
CreateNot
(
expr_
);
break
;
AllocaInst
*
dst
=
b_
.
CreateAllocaBPF
(
unop
.
expr
->
type
,
"deref"
);
case
bpftrace
:
:
Parser
::
token
::
BNOT
:
expr_
=
b_
.
CreateNeg
(
expr_
);
break
;
b_
.
CreateProbeRead
(
dst
,
unop
.
expr
->
type
.
size
,
expr_
);
case
bpftrace
:
:
Parser
::
token
::
MUL
:
expr_
=
b_
.
CreateLoad
(
dst
);
{
b_
.
CreateLifetimeEnd
(
dst
);
int
size
=
type
.
size
;
break
;
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
)
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
)
void
CodegenLLVM
::
visit
(
Cast
&
cast
)
{
{
// TODO
cast
.
expr
->
accept
(
*
this
);
}
}
void
CodegenLLVM
::
visit
(
ExprStatement
&
expr
)
void
CodegenLLVM
::
visit
(
ExprStatement
&
expr
)
...
@@ -336,12 +416,32 @@ void CodegenLLVM::visit(AssignMapStatement &assignment)
...
@@ -336,12 +416,32 @@ void CodegenLLVM::visit(AssignMapStatement &assignment)
Value
*
val
,
*
expr
;
Value
*
val
,
*
expr
;
expr
=
expr_
;
expr
=
expr_
;
AllocaInst
*
key
=
getMapKey
(
map
);
AllocaInst
*
key
=
getMapKey
(
map
);
if
(
assignment
.
expr
->
type
.
type
==
Type
::
string
)
if
(
map
.
type
.
type
==
Type
::
string
)
{
{
val
=
expr
;
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
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"
);
val
=
b_
.
CreateAllocaBPF
(
map
.
type
,
map
.
ident
+
"_val"
);
b_
.
CreateStore
(
expr
,
val
);
b_
.
CreateStore
(
expr
,
val
);
}
}
...
@@ -425,7 +525,7 @@ AllocaInst *CodegenLLVM::getMapKey(Map &map)
...
@@ -425,7 +525,7 @@ AllocaInst *CodegenLLVM::getMapKey(Map &map)
{
{
size
+=
expr
->
type
.
size
;
size
+=
expr
->
type
.
size
;
}
}
key
=
b_
.
CreateAlloca
MapKey
(
size
,
map
.
ident
+
"_key"
);
key
=
b_
.
CreateAlloca
BPF
(
size
,
map
.
ident
+
"_key"
);
int
offset
=
0
;
int
offset
=
0
;
for
(
Expression
*
expr
:
*
map
.
vargs
)
{
for
(
Expression
*
expr
:
*
map
.
vargs
)
{
...
@@ -455,7 +555,7 @@ AllocaInst *CodegenLLVM::getQuantizeMapKey(Map &map, Value *log2)
...
@@ -455,7 +555,7 @@ AllocaInst *CodegenLLVM::getQuantizeMapKey(Map &map, Value *log2)
{
{
size
+=
expr
->
type
.
size
;
size
+=
expr
->
type
.
size
;
}
}
key
=
b_
.
CreateAlloca
MapKey
(
size
,
map
.
ident
+
"_key"
);
key
=
b_
.
CreateAlloca
BPF
(
size
,
map
.
ident
+
"_key"
);
int
offset
=
0
;
int
offset
=
0
;
for
(
Expression
*
expr
:
*
map
.
vargs
)
{
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
...
@@ -48,7 +48,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, const std::str
return
CreateAllocaBPF
(
ty
,
name
);
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
);
llvm
::
Type
*
ty
=
ArrayType
::
get
(
getInt8Ty
(),
bytes
);
return
CreateAllocaBPF
(
ty
,
name
);
return
CreateAllocaBPF
(
ty
,
name
);
...
@@ -57,7 +57,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name)
...
@@ -57,7 +57,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name)
llvm
::
Type
*
IRBuilderBPF
::
GetType
(
const
SizedType
&
stype
)
llvm
::
Type
*
IRBuilderBPF
::
GetType
(
const
SizedType
&
stype
)
{
{
llvm
::
Type
*
ty
;
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
);
ty
=
ArrayType
::
get
(
getInt8Ty
(),
stype
.
size
);
}
}
...
@@ -121,22 +121,23 @@ Value *IRBuilderBPF::CreateMapLookupElem(Map &map, AllocaInst *key)
...
@@ -121,22 +121,23 @@ Value *IRBuilderBPF::CreateMapLookupElem(Map &map, AllocaInst *key)
CreateCondBr
(
condition
,
lookup_success_block
,
lookup_failure_block
);
CreateCondBr
(
condition
,
lookup_success_block
,
lookup_failure_block
);
SetInsertPoint
(
lookup_success_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
);
CreateMemCpy
(
value
,
call
,
map
.
type
.
size
,
1
);
else
else
CreateStore
(
CreateLoad
(
getInt64Ty
(),
call
),
value
);
CreateStore
(
CreateLoad
(
getInt64Ty
(),
call
),
value
);
CreateBr
(
lookup_merge_block
);
CreateBr
(
lookup_merge_block
);
SetInsertPoint
(
lookup_failure_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
);
CreateMemSet
(
value
,
getInt8
(
0
),
map
.
type
.
size
,
1
);
else
else
CreateStore
(
getInt64
(
0
),
value
);
CreateStore
(
getInt64
(
0
),
value
);
CreateBr
(
lookup_merge_block
);
CreateBr
(
lookup_merge_block
);
SetInsertPoint
(
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
value
;
return
CreateLoad
(
value
);
return
CreateLoad
(
value
);
}
}
...
...
src/ast/irbuilderbpf.h
View file @
5c31a0a6
...
@@ -20,7 +20,7 @@ public:
...
@@ -20,7 +20,7 @@ public:
AllocaInst
*
CreateAllocaBPF
(
llvm
::
Type
*
ty
,
const
std
::
string
&
name
=
""
);
AllocaInst
*
CreateAllocaBPF
(
llvm
::
Type
*
ty
,
const
std
::
string
&
name
=
""
);
AllocaInst
*
CreateAllocaBPF
(
const
SizedType
&
stype
,
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
);
llvm
::
Type
*
GetType
(
const
SizedType
&
stype
);
CallInst
*
CreateBpfPseudoCall
(
int
mapfd
);
CallInst
*
CreateBpfPseudoCall
(
int
mapfd
);
CallInst
*
CreateBpfPseudoCall
(
Map
&
map
);
CallInst
*
CreateBpfPseudoCall
(
Map
&
map
);
...
...
src/ast/printer.cpp
View file @
5c31a0a6
...
@@ -101,7 +101,10 @@ void Printer::visit(FieldAccess &acc)
...
@@ -101,7 +101,10 @@ void Printer::visit(FieldAccess &acc)
void
Printer
::
visit
(
Cast
&
cast
)
void
Printer
::
visit
(
Cast
&
cast
)
{
{
std
::
string
indent
(
depth_
,
' '
);
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_
;
++
depth_
;
cast
.
expr
->
accept
(
*
this
);
cast
.
expr
->
accept
(
*
this
);
...
...
src/ast/semantic_analyser.cpp
View file @
5c31a0a6
...
@@ -231,22 +231,31 @@ void SemanticAnalyser::visit(Unop &unop)
...
@@ -231,22 +231,31 @@ void SemanticAnalyser::visit(Unop &unop)
{
{
unop
.
expr
->
accept
(
*
this
);
unop
.
expr
->
accept
(
*
this
);
SizedType
&
type
=
unop
.
expr
->
type
;
if
(
is_final_pass
()
&&
if
(
is_final_pass
()
&&
unop
.
expr
->
type
.
type
!=
Type
::
integer
&&
!
(
type
.
type
==
Type
::
integer
)
&&
unop
.
expr
->
type
.
type
!=
Type
::
cast
)
{
!
(
type
.
type
==
Type
::
cast
&&
unop
.
op
==
Parser
::
token
::
MUL
)
)
{
err_
<<
"The "
<<
opstr
(
unop
)
<<
" operator can not be used on expressions of type '"
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
)
{
if
(
unop
.
op
==
Parser
::
token
::
MUL
)
{
std
::
string
cast_type
=
unop
.
expr
->
type
.
cast_type
;
if
(
type
.
type
==
Type
::
cast
)
{
if
(
cast_type
.
back
()
==
'*'
)
{
if
(
type
.
is_pointer
)
{
cast_type
.
pop_back
();
if
(
bpftrace_
.
structs_
.
count
(
type
.
cast_type
)
==
0
)
{
unop
.
type
=
SizedType
(
Type
::
cast
,
8
,
cast_type
);
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
{
else
if
(
type
.
type
==
Type
::
integer
)
{
err_
<<
"Can not dereference struct/union of type '"
<<
cast_type
<<
"'. "
unop
.
type
=
SizedType
(
Type
::
integer
,
type
.
size
);
<<
"It is not a pointer."
<<
std
::
endl
;
}
}
}
}
else
{
else
{
...
@@ -258,30 +267,35 @@ void SemanticAnalyser::visit(FieldAccess &acc)
...
@@ -258,30 +267,35 @@ void SemanticAnalyser::visit(FieldAccess &acc)
{
{
acc
.
expr
->
accept
(
*
this
);
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
())
{
if
(
is_final_pass
())
{
err_
<<
"Can not access field '"
<<
acc
.
field
err_
<<
"Can not access field '"
<<
acc
.
field
<<
"' on expression of type '"
<<
acc
.
expr
->
type
<<
"' on expression of type '"
<<
type
<<
"'"
<<
std
::
endl
;
<<
"'"
<<
std
::
endl
;
}
}
return
;
return
;
}
}
std
::
string
cast_type
=
acc
.
expr
->
type
.
cast_type
;
if
(
type
.
is_pointer
)
{
if
(
cast_type
.
back
()
==
'*'
)
{
err_
<<
"Can not access field '"
<<
acc
.
field
<<
"' on type '"
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
;
<<
std
::
endl
;
return
;
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
)
{
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
;
<<
"a field named '"
<<
acc
.
field
<<
"'"
<<
std
::
endl
;
}
}
else
{
else
{
acc
.
type
=
fields
[
acc
.
field
].
type
;
acc
.
type
=
fields
[
acc
.
field
].
type
;
acc
.
type
.
is_internal
=
type
.
is_internal
;
}
}
}
}
...
@@ -289,22 +303,20 @@ void SemanticAnalyser::visit(Cast &cast)
...
@@ -289,22 +303,20 @@ void SemanticAnalyser::visit(Cast &cast)
{
{
cast
.
expr
->
accept
(
*
this
);
cast
.
expr
->
accept
(
*
this
);
std
::
string
cast_type
=
cast
.
cast_type
;
if
(
bpftrace_
.
structs_
.
count
(
cast
.
cast_type
)
==
0
)
{
if
(
cast_type
.
back
()
==
'*'
)
err_
<<
"Unknown struct/union: '"
<<
cast
.
cast_type
<<
"'"
<<
std
::
endl
;
cast_type
.
pop_back
();
if
(
bpftrace_
.
structs_
.
count
(
cast_type
)
==
0
)
{
err_
<<
"Unknown struct/union: '"
<<
cast_type
<<
"'"
<<
std
::
endl
;
return
;
return
;
}
}
int
cast_size
;
int
cast_size
;
if
(
cast
.
cast_type
.
back
()
==
'*'
)
{
if
(
cast
.
is_pointer
)
{
cast_size
=
sizeof
(
uintptr_t
);
cast_size
=
sizeof
(
uintptr_t
);
}
}
else
{
else
{
cast_size
=
bpftrace_
.
structs_
[
cast
.
cast_type
].
size
;
cast_size
=
bpftrace_
.
structs_
[
cast
.
cast_type
].
size
;
}
}
cast
.
type
=
SizedType
(
Type
::
cast
,
cast_size
,
cast
.
cast_type
);
cast
.
type
=
SizedType
(
Type
::
cast
,
cast_size
,
cast
.
cast_type
);
cast
.
type
.
is_pointer
=
cast
.
is_pointer
;
}
}
void
SemanticAnalyser
::
visit
(
ExprStatement
&
expr
)
void
SemanticAnalyser
::
visit
(
ExprStatement
&
expr
)
...
@@ -338,6 +350,11 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
...
@@ -338,6 +350,11 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
else
{
else
{
// This map hasn't been seen before
// This map hasn't been seen before
map_val_
.
insert
({
map_ident
,
assignment
.
expr
->
type
});
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
)
{
if
(
assignment
.
expr
->
type
.
type
==
Type
::
cast
)
{
...
@@ -351,6 +368,7 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
...
@@ -351,6 +368,7 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
}
}
else
{
else
{
map_val_
[
map_ident
].
cast_type
=
cast_type
;
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);
...
@@ -93,7 +93,6 @@ void yyerror(bpftrace::Driver &driver, const char *s);
%type <ast::AttachPointList *> attach_points
%type <ast::AttachPointList *> attach_points
%type <ast::AttachPoint *> attach_point
%type <ast::AttachPoint *> attach_point
%type <std::string> wildcard
%type <std::string> wildcard
%type <std::string> type
%type <std::string> ident
%type <std::string> ident
%right ASSIGN
%right ASSIGN
...
@@ -190,11 +189,8 @@ expr : INT { $$ = new ast::Integer($1); }
...
@@ -190,11 +189,8 @@ expr : INT { $$ = new ast::Integer($1); }
| MUL expr %prec DEREF { $$ = new ast::Unop(token::MUL, $2); }
| MUL expr %prec DEREF { $$ = new ast::Unop(token::MUL, $2); }
| expr DOT ident { $$ = new ast::FieldAccess($1, $3); }
| expr DOT ident { $$ = new ast::FieldAccess($1, $3); }
| expr PTR ident { $$ = new ast::FieldAccess(new ast::Unop(token::MUL, $1), $3); }
| expr PTR ident { $$ = new ast::FieldAccess(new ast::Unop(token::MUL, $1), $3); }
| "(" type ")" expr %prec CAST { $$ = new ast::Cast($2, $4); }
| "(" IDENT ")" expr %prec CAST { $$ = new ast::Cast($2, false, $4); }
;
| "(" IDENT MUL ")" expr %prec CAST { $$ = new ast::Cast($2, true, $5); }
type : IDENT { $$ = $1; }
| IDENT MUL { $$ = $1 + "*"; }
;
;
ident : IDENT { $$ = $1; }
ident : IDENT { $$ = $1; }
...
...
src/types.cpp
View file @
5c31a0a6
...
@@ -13,6 +13,8 @@ std::ostream &operator<<(std::ostream &os, Type type)
...
@@ -13,6 +13,8 @@ std::ostream &operator<<(std::ostream &os, Type type)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
SizedType
&
type
)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
SizedType
&
type
)
{
{
os
<<
type
.
type
;
os
<<
type
.
type
;
if
(
type
.
is_pointer
)
os
<<
"*"
;
return
os
;
return
os
;
}
}
...
...
src/types.h
View file @
5c31a0a6
...
@@ -36,6 +36,9 @@ public:
...
@@ -36,6 +36,9 @@ public:
Type
type
;
Type
type
;
size_t
size
;
size_t
size
;
std
::
string
cast_type
;
std
::
string
cast_type
;
bool
is_internal
=
false
;
bool
is_pointer
=
false
;
size_t
pointee_size
;
bool
operator
==
(
const
SizedType
&
t
)
const
;
bool
operator
==
(
const
SizedType
&
t
)
const
;
};
};
...
...
tests/codegen.cpp
View file @
5c31a0a6
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
#include "gtest/gtest.h"
#include "gtest/gtest.h"
#include "bpforc.h"
#include "bpforc.h"
#include "bpftrace.h"
#include "bpftrace.h"
#include "clang_parser.h"
#include "codegen_llvm.h"
#include "codegen_llvm.h"
#include "driver.h"
#include "driver.h"
#include "fake_map.h"
#include "fake_map.h"
...
@@ -46,6 +47,9 @@ void test(const std::string &input, const std::string expected_output)
...
@@ -46,6 +47,9 @@ void test(const std::string &input, const std::string expected_output)
ASSERT_EQ
(
driver
.
parse_str
(
input
),
0
);
ASSERT_EQ
(
driver
.
parse_str
(
input
),
0
);
ClangParser
clang
;
clang
.
parse
(
driver
.
root_
,
bpftrace
.
structs_
);
ast
::
SemanticAnalyser
semantics
(
driver
.
root_
,
bpftrace
);
ast
::
SemanticAnalyser
semantics
(
driver
.
root_
,
bpftrace
);
ASSERT_EQ
(
semantics
.
analyse
(),
0
);
ASSERT_EQ
(
semantics
.
analyse
(),
0
);
ASSERT_EQ
(
semantics
.
create_maps
(
true
),
0
);
ASSERT_EQ
(
semantics
.
create_maps
(
true
),
0
);
...
@@ -1322,6 +1326,634 @@ attributes #1 = { argmemonly nounwind }
...
@@ -1322,6 +1326,634 @@ attributes #1 = { argmemonly nounwind }
)EXPECTED"
);
)EXPECTED"
);
}
}
TEST
(
codegen
,
struct_integers
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%Foo.x = alloca i32, align 4
%1 = bitcast i32* %Foo.x to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i32* nonnull %Foo.x, i64 4, i64 0)
%2 = load i32, i32* %Foo.x, align 4
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
%3 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
store i64 0, i64* %"@x_key", align 8
%4 = zext i32 %2 to i64
%5 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
store i64 %4, i64* %"@x_val", align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { int x; }"
"kprobe:f"
"{"
" $foo = (Foo)0;"
" @x = $foo.x;"
"}"
,
expected
);
test
(
"struct Foo { int x; }"
"kprobe:f"
"{"
" $foo = (Foo*)0;"
" @x = $foo->x;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_integer_ptr
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%deref = alloca i32, align 4
%Foo.x = alloca i64, align 8
%1 = bitcast i64* %Foo.x to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i64* nonnull %Foo.x, i64 8, i64 0)
%2 = load i64, i64* %Foo.x, align 8
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
%3 = bitcast i32* %deref to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
%probe_read1 = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i32* nonnull %deref, i64 4, i64 %2)
%4 = load i32, i32* %deref, align 4
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
%5 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
store i64 0, i64* %"@x_key", align 8
%6 = zext i32 %4 to i64
%7 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %7)
store i64 %6, i64* %"@x_val", align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %7)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { int *x; }"
"kprobe:f"
"{"
" $foo = (Foo)0;"
" @x = *$foo.x;"
"}"
,
expected
);
test
(
"struct Foo { int *x; }"
"kprobe:f"
"{"
" $foo = (Foo*)0;"
" @x = *$foo->x;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_string_ptr
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@mystr_key" = alloca i64, align 8
%Foo.str = alloca i64, align 8
%str = alloca [64 x i8], align 1
%1 = getelementptr inbounds [64 x i8], [64 x i8]* %str, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
call void @llvm.memset.p0i8.i64(i8* nonnull %1, i8 0, i64 64, i32 1, i1 false)
%2 = bitcast i64* %Foo.str to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i64* nonnull %Foo.str, i64 8, i64 0)
%3 = load i64, i64* %Foo.str, align 8
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2)
%probe_read_str = call i64 inttoptr (i64 45 to i64 (i8*, i64, i8*)*)([64 x i8]* nonnull %str, i64 64, i64 %3)
%4 = bitcast i64* %"@mystr_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %4)
store i64 0, i64* %"@mystr_key", align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@mystr_key", [64 x i8]* nonnull %str, i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %4)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) #1
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { char *str; }"
"kprobe:f"
"{"
" $foo = (Foo)0;"
" @mystr = str($foo.str);"
"}"
,
expected
);
test
(
"struct Foo { char *str; }"
"kprobe:f"
"{"
" $foo = (Foo*)0;"
" @mystr = str($foo->str);"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_string_array
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@mystr_key" = alloca i64, align 8
%Foo.str = alloca [32 x i8], align 1
%1 = getelementptr inbounds [32 x i8], [32 x i8]* %Foo.str, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)([32 x i8]* nonnull %Foo.str, i64 32, i64 0)
%2 = bitcast i64* %"@mystr_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2)
store i64 0, i64* %"@mystr_key", align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@mystr_key", [32 x i8]* nonnull %Foo.str, i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { char str[32]; }"
"kprobe:f"
"{"
" $foo = (Foo)0;"
" @mystr = $foo.str;"
"}"
,
expected
);
test
(
"struct Foo { char str[32]; }"
"kprobe:f"
"{"
" $foo = (Foo*)0;"
" @mystr = $foo->str;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_nested_struct_named
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%Bar.x = alloca i32, align 4
%1 = bitcast i32* %Bar.x to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i32* nonnull %Bar.x, i64 4, i64 0)
%2 = load i32, i32* %Bar.x, align 4
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
%3 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
store i64 0, i64* %"@x_key", align 8
%4 = zext i32 %2 to i64
%5 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
store i64 %4, i64* %"@x_val", align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Bar { int x; } struct Foo { struct Bar bar; }"
"kprobe:f"
"{"
" $foo = (Foo)0;"
" @x = $foo.bar.x;"
"}"
,
expected
);
test
(
"struct Bar { int x; } struct Foo { struct Bar bar; }"
"kprobe:f"
"{"
" $foo = (Foo*)0;"
" @x = $foo->bar.x;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_nested_struct_ptr_named
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%Bar.x = alloca i32, align 4
%Foo.bar = alloca i64, align 8
%1 = bitcast i64* %Foo.bar to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i64* nonnull %Foo.bar, i64 8, i64 0)
%2 = load i64, i64* %Foo.bar, align 8
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
%3 = bitcast i32* %Bar.x to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
%probe_read1 = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i32* nonnull %Bar.x, i64 4, i64 %2)
%4 = load i32, i32* %Bar.x, align 4
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
%5 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
store i64 0, i64* %"@x_key", align 8
%6 = zext i32 %4 to i64
%7 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %7)
store i64 %6, i64* %"@x_val", align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %7)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Bar { int x; } struct Foo { struct Bar *bar; }"
"kprobe:f"
"{"
" $foo = (Foo)0;"
" @x = $foo.bar->x;"
"}"
,
expected
);
test
(
"struct Bar { int x; } struct Foo { struct Bar *bar; }"
"kprobe:f"
"{"
" $foo = (Foo*)0;"
" @x = $foo->bar->x;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_nested_struct_anon
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%"Foo::(anonymous at definitions.h:1:14).x" = alloca i32, align 4
%1 = bitcast i32* %"Foo::(anonymous at definitions.h:1:14).x" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)(i32* nonnull %"Foo::(anonymous at definitions.h:1:14).x", i64 4, i64 0)
%2 = load i32, i32* %"Foo::(anonymous at definitions.h:1:14).x", align 4
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
%3 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
store i64 0, i64* %"@x_key", align 8
%4 = zext i32 %2 to i64
%5 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
store i64 %4, i64* %"@x_val", align 8
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { struct { int x; } bar; }"
"kprobe:f"
"{"
" $foo = (Foo)0;"
" @x = $foo.bar.x;"
"}"
,
expected
);
test
(
"struct Foo { struct { int x; } bar; }"
"kprobe:f"
"{"
" $foo = (Foo*)0;"
" @x = $foo->bar.x;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_save
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@foo_val" = alloca [12 x i8], align 1
%"@foo_key" = alloca i64, align 8
%1 = bitcast i64* %"@foo_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 0, i64* %"@foo_key", align 8
%2 = getelementptr inbounds [12 x i8], [12 x i8]* %"@foo_val", i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)([12 x i8]* nonnull %"@foo_val", i64 12, i64 0)
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@foo_key", [12 x i8]* nonnull %"@foo_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { int x, y, z; }"
"kprobe:f"
"{"
" @foo = (Foo)0;"
"}"
,
expected
);
test
(
"struct Foo { int x, y, z; }"
"kprobe:f"
"{"
" @foo = *(Foo*)0;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_save_nested
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@x_val" = alloca i64, align 8
%"@x_key" = alloca i64, align 8
%"@foo_key5" = alloca i64, align 8
%"@bar_key" = alloca i64, align 8
%internal_Foo.bar = alloca i64, align 8
%tmpcast = bitcast i64* %internal_Foo.bar to [8 x i8]*
%"@foo_key1" = alloca i64, align 8
%"@foo_val" = alloca [16 x i8], align 1
%"@foo_key" = alloca i64, align 8
%1 = bitcast i64* %"@foo_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 0, i64* %"@foo_key", align 8
%2 = getelementptr inbounds [16 x i8], [16 x i8]* %"@foo_val", i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)([16 x i8]* nonnull %"@foo_val", i64 16, i64 0)
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 2)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@foo_key", [16 x i8]* nonnull %"@foo_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2)
%3 = bitcast i64* %"@foo_key1" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
store i64 0, i64* %"@foo_key1", align 8
%pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 2)
%lookup_elem = call i8* inttoptr (i64 1 to i8* (i8*, i8*)*)(i64 %pseudo2, i64* nonnull %"@foo_key1")
%map_lookup_cond = icmp eq i8* %lookup_elem, null
br i1 %map_lookup_cond, label %lookup_merge, label %lookup_success
lookup_success: ; preds = %entry
%lookup_elem_val.sroa.3.0.lookup_elem.sroa_idx = getelementptr inbounds i8, i8* %lookup_elem, i64 4
%lookup_elem_val.sroa.3.0.lookup_elem.sroa_cast = bitcast i8* %lookup_elem_val.sroa.3.0.lookup_elem.sroa_idx to i64*
%lookup_elem_val.sroa.3.0.copyload = load i64, i64* %lookup_elem_val.sroa.3.0.lookup_elem.sroa_cast, align 1
br label %lookup_merge
lookup_merge: ; preds = %entry, %lookup_success
%lookup_elem_val.sroa.3.0 = phi i64 [ %lookup_elem_val.sroa.3.0.copyload, %lookup_success ], [ 0, %entry ]
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
%4 = bitcast i64* %internal_Foo.bar to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %4)
store i64 %lookup_elem_val.sroa.3.0, i64* %internal_Foo.bar, align 8
%5 = bitcast i64* %"@bar_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
store i64 0, i64* %"@bar_key", align 8
%pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem4 = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo3, i64* nonnull %"@bar_key", [8 x i8]* nonnull %tmpcast, i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %4)
%6 = bitcast i64* %"@foo_key5" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %6)
store i64 0, i64* %"@foo_key5", align 8
%pseudo6 = call i64 @llvm.bpf.pseudo(i64 1, i64 2)
%lookup_elem7 = call i8* inttoptr (i64 1 to i8* (i8*, i8*)*)(i64 %pseudo6, i64* nonnull %"@foo_key5")
%map_lookup_cond12 = icmp eq i8* %lookup_elem7, null
br i1 %map_lookup_cond12, label %lookup_merge10, label %lookup_success8
lookup_success8: ; preds = %lookup_merge
%lookup_elem_val11.sroa.3.0.lookup_elem7.sroa_idx = getelementptr inbounds i8, i8* %lookup_elem7, i64 4
%lookup_elem_val11.sroa.3.0.lookup_elem7.sroa_cast = bitcast i8* %lookup_elem_val11.sroa.3.0.lookup_elem7.sroa_idx to i64*
%lookup_elem_val11.sroa.3.0.copyload = load i64, i64* %lookup_elem_val11.sroa.3.0.lookup_elem7.sroa_cast, align 1
%phitmp17 = and i64 %lookup_elem_val11.sroa.3.0.copyload, 4294967295
br label %lookup_merge10
lookup_merge10: ; preds = %lookup_merge, %lookup_success8
%lookup_elem_val11.sroa.3.0 = phi i64 [ %phitmp17, %lookup_success8 ], [ 0, %lookup_merge ]
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %6)
%7 = bitcast i64* %"@x_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %7)
store i64 0, i64* %"@x_key", align 8
%8 = bitcast i64* %"@x_val" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %8)
store i64 %lookup_elem_val11.sroa.3.0, i64* %"@x_val", align 8
%pseudo14 = call i64 @llvm.bpf.pseudo(i64 1, i64 3)
%update_elem15 = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo14, i64* nonnull %"@x_key", i64* nonnull %"@x_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %7)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %8)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { int m; struct { int x; int y; } bar; int n; }"
"kprobe:f"
"{"
" @foo = (Foo)0;"
" @bar = @foo.bar;"
" @x = @foo.bar.x;"
"}"
,
expected
);
}
TEST
(
codegen
,
struct_save_string
)
{
auto
expected
=
R"EXPECTED(; Function Attrs: nounwind
declare i64 @llvm.bpf.pseudo(i64, i64) #0
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
define i64 @"kprobe:f"(i8* nocapture readnone) local_unnamed_addr section "s_kprobe:f" {
entry:
%"@str_key" = alloca i64, align 8
%lookup_elem_val = alloca [32 x i8], align 1
%"@foo_key1" = alloca i64, align 8
%"@foo_val" = alloca [32 x i8], align 1
%"@foo_key" = alloca i64, align 8
%1 = bitcast i64* %"@foo_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %1)
store i64 0, i64* %"@foo_key", align 8
%2 = getelementptr inbounds [32 x i8], [32 x i8]* %"@foo_val", i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %2)
%probe_read = call i64 inttoptr (i64 4 to i64 (i8*, i64, i8*)*)([32 x i8]* nonnull %"@foo_val", i64 32, i64 0)
%pseudo = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%update_elem = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo, i64* nonnull %"@foo_key", [32 x i8]* nonnull %"@foo_val", i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %1)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %2)
%3 = bitcast i64* %"@foo_key1" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %3)
store i64 0, i64* %"@foo_key1", align 8
%pseudo2 = call i64 @llvm.bpf.pseudo(i64 1, i64 1)
%lookup_elem = call i8* inttoptr (i64 1 to i8* (i8*, i8*)*)(i64 %pseudo2, i64* nonnull %"@foo_key1")
%4 = getelementptr inbounds [32 x i8], [32 x i8]* %lookup_elem_val, i64 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %4)
%map_lookup_cond = icmp eq i8* %lookup_elem, null
br i1 %map_lookup_cond, label %lookup_failure, label %lookup_success
lookup_success: ; preds = %entry
call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull %4, i8* nonnull %lookup_elem, i64 32, i32 1, i1 false)
br label %lookup_merge
lookup_failure: ; preds = %entry
call void @llvm.memset.p0i8.i64(i8* nonnull %4, i8 0, i64 32, i32 1, i1 false)
br label %lookup_merge
lookup_merge: ; preds = %lookup_failure, %lookup_success
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %3)
%5 = bitcast i64* %"@str_key" to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %5)
store i64 0, i64* %"@str_key", align 8
%pseudo3 = call i64 @llvm.bpf.pseudo(i64 1, i64 2)
%update_elem4 = call i64 inttoptr (i64 2 to i64 (i8*, i8*, i8*, i64)*)(i64 %pseudo3, i64* nonnull %"@str_key", i8* nonnull %4, i64 0)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %5)
call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %4)
ret i64 0
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) #1
; Function Attrs: argmemonly nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) #1
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
)EXPECTED"
;
test
(
"struct Foo { char str[32]; }"
"kprobe:f"
"{"
" @foo = (Foo)0;"
" @str = @foo.str;"
"}"
,
expected
);
}
}
// namespace codegen
}
// namespace codegen
}
// namespace test
}
// namespace test
}
// namespace bpftrace
}
// namespace bpftrace
tests/semantic_analyser.cpp
View file @
5c31a0a6
#include "gmock/gmock.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "gtest/gtest.h"
#include "bpftrace.h"
#include "bpftrace.h"
#include "clang_parser.h"
#include "driver.h"
#include "driver.h"
#include "semantic_analyser.h"
#include "semantic_analyser.h"
...
@@ -19,6 +20,9 @@ void test(BPFtrace &bpftrace, Driver &driver, const std::string &input, int expe
...
@@ -19,6 +20,9 @@ void test(BPFtrace &bpftrace, Driver &driver, const std::string &input, int expe
{
{
ASSERT_EQ
(
driver
.
parse_str
(
input
),
0
);
ASSERT_EQ
(
driver
.
parse_str
(
input
),
0
);
ClangParser
clang
;
clang
.
parse
(
driver
.
root_
,
bpftrace
.
structs_
);
std
::
stringstream
out
;
std
::
stringstream
out
;
ast
::
SemanticAnalyser
semantics
(
driver
.
root_
,
bpftrace
,
out
);
ast
::
SemanticAnalyser
semantics
(
driver
.
root_
,
bpftrace
,
out
);
std
::
stringstream
msg
;
std
::
stringstream
msg
;
...
@@ -40,21 +44,7 @@ void test(Driver &driver, const std::string &input, int expected_result=0)
...
@@ -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
)
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
bpftrace
;
bpftrace
.
structs_
[
"type1"
]
=
type1
;
bpftrace
.
structs_
[
"type2"
]
=
type2
;
Driver
driver
;
Driver
driver
;
test
(
bpftrace
,
driver
,
input
,
expected_result
);
test
(
bpftrace
,
driver
,
input
,
expected_result
);
}
}
...
@@ -217,6 +207,34 @@ TEST(semantic_analyser, variable_type)
...
@@ -217,6 +207,34 @@ TEST(semantic_analyser, variable_type)
EXPECT_EQ
(
st
,
assignment
->
var
->
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
(
semantic_analyser
,
printf
)
{
{
test
(
"kprobe:f { printf(
\"
hi
\"
) }"
,
0
);
test
(
"kprobe:f { printf(
\"
hi
\"
) }"
,
0
);
...
@@ -370,24 +388,28 @@ TEST(semantic_analyser, profile)
...
@@ -370,24 +388,28 @@ TEST(semantic_analyser, profile)
TEST
(
semantic_analyser
,
variable_cast_types
)
TEST
(
semantic_analyser
,
variable_cast_types
)
{
{
test
(
"kprobe:f { $x = (type1)cpu; $x = (type1)cpu; }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; } struct type2 { int field; }"
;
test
(
"kprobe:f { $x = (type1)cpu; $x = (type2)cpu; }"
,
1
);
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
(
semantic_analyser
,
map_cast_types
)
{
{
test
(
"kprobe:f { @x = (type1)cpu; @x = (type1)cpu; }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; } struct type2 { int field; }"
;
test
(
"kprobe:f { @x = (type1)cpu; @x = (type2)cpu; }"
,
1
);
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
(
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
(
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
)
TEST
(
semantic_analyser
,
cast_unknown_type
)
...
@@ -397,50 +419,76 @@ TEST(semantic_analyser, cast_unknown_type)
...
@@ -397,50 +419,76 @@ TEST(semantic_analyser, cast_unknown_type)
TEST
(
semantic_analyser
,
field_access
)
TEST
(
semantic_analyser
,
field_access
)
{
{
test
(
"kprobe:f { ((type1)cpu).field }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; }"
;
test
(
"kprobe:f { $x = (type1)cpu; $x.field }"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)cpu).field }"
,
0
);
test
(
"kprobe:f { @x = (type1)cpu; @x.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
(
semantic_analyser
,
field_access_wrong_field
)
{
{
test
(
"kprobe:f { ((type1)cpu).blah }"
,
1
);
std
::
string
structs
=
"struct type1 { int field; }"
;
test
(
"kprobe:f { $x = (type1)cpu; $x.blah }"
,
1
);
test
(
structs
+
"kprobe:f { ((type1)cpu).blah }"
,
1
);
test
(
"kprobe:f { @x = (type1)cpu; @x.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
(
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
(
semantic_analyser
,
field_access_types
)
{
{
test
(
"kprobe:f { ((type1)0).field == 123 }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; char mystr[8]; }"
test
(
"kprobe:f { ((type1)0).field ==
\"
abc
\"
}"
,
10
)
;
"struct type2 { int field; }"
;
test
(
"kprobe:f { ((type1)0).mystr ==
\"
abc
\"
}"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)0).field == 123
}"
,
0
);
test
(
"kprobe:f { ((type1)0).mystr == 123
}"
,
10
);
test
(
structs
+
"kprobe:f { ((type1)0).field ==
\"
abc
\"
}"
,
10
);
test
(
"kprobe:f { ((type1)0).field == ((type2)0).field }"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)0).mystr ==
\"
abc
\"
}"
,
0
);
test
(
"kprobe:f { ((type1)0).mystr == ((type2)0).field }"
,
10
);
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
(
semantic_analyser
,
field_access_pointer
)
{
{
test
(
"kprobe:f { ((type1*)0)->field }"
,
0
);
std
::
string
structs
=
"struct type1 { int field; }"
;
test
(
"kprobe:f { ((type1*)0).field }"
,
1
);
test
(
structs
+
"kprobe:f { ((type1*)0)->field }"
,
0
);
test
(
"kprobe:f { *((type1*)0) }"
,
0
);
test
(
structs
+
"kprobe:f { ((type1*)0).field }"
,
1
);
test
(
structs
+
"kprobe:f { *((type1*)0) }"
,
0
);
}
}
TEST
(
semantic_analyser
,
field_access_sub_struct
)
TEST
(
semantic_analyser
,
field_access_sub_struct
)
{
{
test
(
"kprobe:f { ((type1)0).type2ptr->field }"
,
0
);
std
::
string
structs
=
"struct type1 { struct type2 *type2ptr; struct type2 type2; }"
test
(
"kprobe:f { ((type1)0).type2.field }"
,
0
);
"struct type2 { int field; }"
;
test
(
"kprobe:f { $x = (type2)0; $x = ((type1)0).type2 }"
,
0
);
test
(
"kprobe:f { $x = (type2*)0; $x = ((type1)0).type2ptr }"
,
0
);
test
(
structs
+
"kprobe:f { ((type1)0).type2ptr->field }"
,
0
);
test
(
"kprobe:f { $x = (type1)0; $x = ((type1)0).type2 }"
,
1
);
test
(
structs
+
"kprobe:f { ((type1)0).type2.field }"
,
0
);
test
(
"kprobe:f { $x = (type1*)0; $x = ((type1)0).type2ptr }"
,
1
);
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
}
// 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