Created
September 19, 2016 01:56
-
-
Save mmha/e874d43199cdfa1fbda164fe9d1fce2f to your computer and use it in GitHub Desktop.
Clang Static Analyzer Plugin for glBegin()/glEnd() (Old toy code, written against Clang 3.6.0)
This file contains hidden or 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
#include "GLBeginEndChecker.h" | |
#include <iostream> | |
REGISTER_MAP_WITH_PROGRAMSTATE(GLBES, int, GLBeginEndState) | |
namespace clang | |
{ | |
namespace ento | |
{ | |
void registerGLBeginEndChecker(CheckerManager &mgr) | |
{ | |
mgr.registerChecker< GLBeginEndChecker >(); | |
} | |
} | |
} | |
GLBeginEndChecker::GLBeginEndChecker() | |
: NoBeginBug(new BugType(this, "No glBegin", "OpenGL Error")) | |
, DoubleBeginBug(new BugType(this, "Double glBegin", "OpenGL Error")) | |
, NoEndBug(new BugType(this, "No glEnd after glBegin", "OpenGL Error")) | |
, InvalidCallInBlockBug(new BugType(this, "Illegal call", "OpenGL Error")) | |
{ | |
} | |
void GLBeginEndChecker::reportNoBegin(const CallEvent &call, CheckerContext &context) const | |
{ | |
auto errNode = context.generateSink(); | |
if(!errNode) | |
return; | |
auto report = new BugReport(*NoBeginBug, "No glBegin preceding glEnd", errNode); | |
report->addRange(call.getSourceRange()); | |
context.emitReport(report); | |
} | |
void GLBeginEndChecker::reportDoubleBegin(const CallEvent &call, CheckerContext &context) const | |
{ | |
auto errNode = context.generateSink(); | |
if(!errNode) | |
return; | |
auto report = new BugReport(*NoBeginBug, "Two glBegin calls without a glEnd call between them", errNode); | |
report->addRange(call.getSourceRange()); | |
context.emitReport(report); | |
} | |
void GLBeginEndChecker::reportNoEnd(const CallEvent &call, CheckerContext &context) const | |
{ | |
auto errNode = context.generateSink(); | |
if(!errNode) | |
return; | |
auto report = new BugReport(*NoBeginBug, "Illegal call in a glBegin/glEnd Block", errNode); | |
report->addRange(call.getSourceRange()); | |
context.emitReport(report); | |
} | |
void GLBeginEndChecker::reportInvalidCallInBlock(const CallEvent &call, CheckerContext &context) const | |
{ | |
// TODO | |
} | |
bool GLBeginEndChecker::hasNoEnd(SymbolRef sym, const GLBeginEndState &state, bool isSymDead) | |
{ | |
if(isSymDead && state.isInBlock()) | |
{ | |
return true; | |
} | |
return false; | |
} | |
void GLBeginEndChecker::checkPostCall(const CallEvent &call, CheckerContext &context) const | |
{ | |
auto programState = context.getState(); | |
const auto glState = programState->get< GLBES >(1); | |
std::cout << call.getCalleeIdentifier()->getName().str() << std::endl; | |
if(call.getCalleeIdentifier()->getName().str() == "glBegin") | |
{ | |
if(glState && glState->isInBlock()) | |
{ | |
std::cout << "Found glBegin\n"; | |
reportDoubleBegin(call, context); | |
return; | |
} | |
programState = programState->set< GLBES >(1, GLBeginEndState::State::IN_BLOCK); | |
context.addTransition(programState); | |
return; | |
} | |
else if(call.getCalleeIdentifier()->getName().str() == "glEnd") | |
{ | |
if(glState && !glState->isInBlock()) | |
{ | |
std::cout << "Found glEnd\n"; | |
reportNoBegin(call, context); | |
return; | |
} | |
} | |
else | |
{ | |
if(glState && glState->isInBlock()) | |
{ | |
const std::array< std::string, 16 > validBlockCalls = {"glVertex", | |
"glColor", | |
"glSecondaryColor", | |
"glIndex", | |
"glNormal", | |
"glFogCoord", | |
"glTexCoord", | |
"glMultiTexCoord", | |
"glVertexAttrib", | |
"glEvalCoord", | |
"glEvalPoint", | |
"glArrayElement", | |
"glMaterial", | |
"glEdgeFlag", | |
"glCallList", | |
"glCallLists"}; | |
auto callName = call.getCalleeIdentifier()->getName(); | |
for(auto &str : validBlockCalls) | |
{ | |
if(!boost::starts_with(callName, str)) | |
{ | |
reportInvalidCallInBlock(call, context); | |
return; | |
} | |
} | |
} | |
} | |
} | |
void GLBeginEndChecker::checkEndAnalysis(ExplodedGraph &graph, BugReporter &reporter, ExprEngine &engine) const | |
{ | |
// TODO | |
} |
This file contains hidden or 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
#ifndef GLBEGINEND_CHECKER_H | |
#define GLBEGINEND_CHECKER_H | |
#include "GLBeginEndState.h" | |
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | |
#include "clang/StaticAnalyzer/Core/Checker.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | |
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | |
#include <boost/algorithm/string/predicate.hpp> | |
#include <memory> | |
#include <string> | |
#include <array> | |
using namespace clang; | |
using namespace ento; | |
class GLBeginEndChecker : public Checker< check::PostCall, check::EndAnalysis > | |
{ | |
std::unique_ptr<BugType> NoBeginBug, DoubleBeginBug, NoEndBug, InvalidCallInBlockBug; | |
std::array<IdentifierInfo, 16> validBlockCallIdentifiers; | |
void reportNoBegin(const CallEvent &call, CheckerContext &context) const; | |
void reportDoubleBegin(const CallEvent &call, CheckerContext &context) const; | |
void reportNoEnd(const CallEvent &call, CheckerContext &context) const; | |
void reportInvalidCallInBlock(const CallEvent &call, CheckerContext &context) const; | |
bool hasNoEnd(SymbolRef sym, const GLBeginEndState &state, bool isSymDead); | |
public: | |
GLBeginEndChecker(); | |
void checkPostCall(const CallEvent &call, CheckerContext &context) const; | |
void checkEndAnalysis(ExplodedGraph &graph, BugReporter &reporter, ExprEngine &engine) const; | |
}; | |
#endif |
This file contains hidden or 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
#ifndef GLBEGINEND_STATE_H | |
#define GLBEGINEND_STATE_H | |
#include <llvm/ADT/FoldingSet.h> | |
class GLBeginEndState | |
{ | |
public: | |
enum class State | |
{ | |
OUT_OF_BLOCK = 0, | |
IN_BLOCK = 1 | |
}; | |
GLBeginEndState(State s) | |
: state(s) | |
{ | |
} | |
bool isInBlock() const | |
{ | |
return state == State::IN_BLOCK; | |
} | |
bool operator==(GLBeginEndState other) const | |
{ | |
return state == other.state; | |
} | |
void Profile(llvm::FoldingSetNodeID &ID) const | |
{ | |
ID.AddInteger((int) state); | |
} | |
protected: | |
State state; | |
}; | |
#endif |
This file contains hidden or 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
#include <GL/glew.h> | |
#include <GL/gl.h> | |
void foo(int x) | |
{ | |
if(x >= 0 && x < 5) | |
{ | |
glBegin(GL_TRIANGLES); | |
} | |
if(x == 1) | |
{ | |
glBegin(GL_TRIANGLES); | |
} | |
if(x == 2) | |
{ | |
glClear(GL_COLOR_BUFFER_BIT); | |
} | |
if(x == 3 || x == 4) | |
{ | |
glEnd(); | |
} | |
if(x == 4) | |
{ | |
glEnd(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment