Skip to content

Commit ade96a6

Browse files
committed
Merging with the "traced" changeset
Conflicts: codepulse/src/main/scala/com/secdec/codepulse/data/trace/TreeBuilder.scala
2 parents 8f8e777 + 548d1ba commit ade96a6

File tree

11 files changed

+111
-39
lines changed

11 files changed

+111
-39
lines changed

codepulse/src/main/scala/com/secdec/codepulse/data/bytecode/CodeForestBuilder.scala

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object CodeForestBuilder {
3131
val JSPGroupName = "JSPs"
3232
}
3333

34-
class CodeForestBuilder(defaultTracedGroups: List[String]) {
34+
class CodeForestBuilder {
3535
import CodeForestBuilder._
3636

3737
private val roots = SortedSet.empty[CodeTreeNode]
@@ -45,18 +45,13 @@ class CodeForestBuilder(defaultTracedGroups: List[String]) {
4545

4646
def result = {
4747
val lb = List.newBuilder[TreeNodeData]
48-
val tracedGroups = defaultTracedGroups.toSet
4948
for (root <- roots) root.visitTree { node =>
5049
val id = node.id
5150
val name = node.name
5251
val parentId = node.parentId
5352
val kind = node.kind
5453
val size = node.size
55-
val traced = kind match {
56-
case CodeTreeNodeKind.Grp | CodeTreeNodeKind.Pkg => Some(tracedGroups contains root.name)
57-
case _ => None
58-
}
59-
lb += TreeNodeData(id, parentId, name, kind, size, traced)
54+
lb += TreeNodeData(id, parentId, name, kind, size)
6055
}
6156
lb.result()
6257
}

codepulse/src/main/scala/com/secdec/codepulse/data/trace/TreeBuilder.scala

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,23 @@ import com.fasterxml.jackson.core.{ JsonFactory, JsonGenerator }
2323

2424
import com.secdec.codepulse.data.bytecode.CodeTreeNodeKind
2525

26-
case class TreeNode(data: TreeNodeData, children: List[TreeNode])
26+
case class TreeNode(data: TreeNodeData, children: List[TreeNode])(treeNodeData: TreeNodeDataAccess) {
27+
import treeNodeData.ExtendedTreeNodeData
28+
29+
def traced = data.traced
30+
}
2731

2832
case class PackageTreeNode(
2933
id: Option[Int],
3034
kind: CodeTreeNodeKind,
3135
label: String,
3236
methodCount: Int,
3337
otherDescendantIds: List[Int],
34-
traced: Option[Boolean],
35-
children: List[PackageTreeNode])
38+
children: List[PackageTreeNode])(
39+
tracedLookup: () => Option[Boolean]) {
40+
41+
def traced = tracedLookup()
42+
}
3643

3744
/** Builds/projects treemap and package tree data as JSON for client.
3845
* TODO: manage lifetime of cached data internally
@@ -60,7 +67,8 @@ class TreeBuilder(treeNodeData: TreeNodeDataAccess) {
6067

6168
def buildNode(id: Int): TreeNode = {
6269
val children = childrenFor(id).result.map(buildNode)
63-
TreeNode(nodeMap(id), children)
70+
val node = nodeMap(id)
71+
TreeNode(node, children)(treeNodeData)
6472
}
6573

6674
(roots.result.map(buildNode), nodeMap)
@@ -69,6 +77,7 @@ class TreeBuilder(treeNodeData: TreeNodeDataAccess) {
6977
/** Full package tree, with self nodes */
7078
lazy val packageTree = {
7179
// build up a package tree with the relevant data
80+
import treeNodeData.ExtendedTreeNodeData
7281

7382
/** A node is eligible for a self node if it is a package node that has at least one
7483
* package child and one non-package child (class/method).
@@ -120,10 +129,10 @@ class TreeBuilder(treeNodeData: TreeNodeDataAccess) {
120129
}
121130

122131
// build the self node
123-
val selfNode = PackageTreeNode(Some(node.data.id), CodeTreeNodeKind.Pkg, "<self>", selfChildren.map(countMethods).sum, otherDescendants, node.data.traced, Nil)
124-
PackageTreeNode(None, node.data.kind, node.data.label, countMethods(node), Nil, node.data.traced, selfNode :: filterChildren(children).map(transform))
132+
val selfNode = PackageTreeNode(Some(node.data.id), CodeTreeNodeKind.Pkg, "<self>", selfChildren.map(countMethods).sum, otherDescendants, Nil)(() => node.data.traced)
133+
PackageTreeNode(None, node.data.kind, node.data.label, countMethods(node), Nil, selfNode :: filterChildren(children).map(transform))(() => node.data.traced)
125134
} else {
126-
PackageTreeNode(Some(node.data.id), node.data.kind, node.data.label, countMethods(node), otherDescendants, node.data.traced, filterChildren(node.children).map(transform))
135+
PackageTreeNode(Some(node.data.id), node.data.kind, node.data.label, countMethods(node), otherDescendants, filterChildren(node.children).map(transform))(() => node.data.traced)
127136
}
128137
}
129138

@@ -165,7 +174,7 @@ class TreeBuilder(treeNodeData: TreeNodeDataAccess) {
165174
}
166175

167176
if (isSelected || !filteredChildren.isEmpty)
168-
Some(TreeNode(node.data, filteredChildren))
177+
Some(TreeNode(node.data, filteredChildren)(treeNodeData))
169178
else
170179
None
171180
} else None

codepulse/src/main/scala/com/secdec/codepulse/data/trace/TreeNodeData.scala

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ import com.secdec.codepulse.data.bytecode.CodeTreeNodeKind
2929
* @param kind The `CodeTreeNodeKind` of this node
3030
* @param size A number indicating the size of the node (e.g. lines of code). If unspecified,
3131
* the size of a node is assumed to be the sum of its childrens' sizes.
32-
* @param traced whether or not this treenode is being traced; this value may be unspecified (None)
3332
*/
34-
case class TreeNodeData(id: Int, parentId: Option[Int], label: String, kind: CodeTreeNodeKind, size: Option[Int], traced: Option[Boolean])
33+
case class TreeNodeData(id: Int, parentId: Option[Int], label: String, kind: CodeTreeNodeKind, size: Option[Int])
3534

3635
/** Access trait for tree node data.
3736
*
@@ -64,9 +63,19 @@ trait TreeNodeDataAccess {
6463
def storeNode(node: TreeNodeData): Unit
6564
def storeNodes(nodes: Iterable[TreeNodeData]) = nodes foreach storeNode
6665

66+
def isTraced(id: Int): Option[Boolean]
67+
def isTraced(node: TreeNodeData): Option[Boolean] = isTraced(node.id)
68+
6769
def updateTraced(id: Int, traced: Option[Boolean]): Unit
6870
def updateTraced(id: Int, traced: Boolean): Unit = updateTraced(id, Some(traced))
6971
def updateTraced(node: TreeNodeData, traced: Option[Boolean]): Unit = updateTraced(node.id, traced)
7072
def updateTraced(node: TreeNodeData, traced: Boolean): Unit = updateTraced(node.id, Some(traced))
7173
def updateTraced(values: Iterable[(Int, Boolean)]): Unit = values foreach { case (id, traced) => updateTraced(id, Some(traced)) }
74+
75+
implicit class ExtendedTreeNodeData(n: TreeNodeData) {
76+
/** whether or not this treenode is being traced; this value may be unspecified (None) */
77+
def traced = isTraced(n)
78+
def traced_=(newVal: Boolean) = updateTraced(n, newVal)
79+
def traced_=(newVal: Option[Boolean]) = updateTraced(n, newVal)
80+
}
7281
}

codepulse/src/main/scala/com/secdec/codepulse/data/trace/slick/SlickTreeNodeDataAccess.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,18 @@ private[slick] class SlickTreeNodeDataAccess(dao: TreeNodeDataDao, db: Database)
101101
dao storeNodes nodes
102102
}
103103

104-
def updateTraced(id: Int, traced: Option[Boolean]) = db withTransaction { implicit transaction =>
105-
dao.updateTraced(id, traced)
104+
private lazy val tracedCache = collection.mutable.Map(db withSession { implicit session => dao.getTraced }: _*)
105+
106+
def isTraced(id: Int): Option[Boolean] = tracedCache.get(id)
107+
108+
def updateTraced(id: Int, traced: Option[Boolean]) = {
109+
db withTransaction { implicit transaction =>
110+
dao.updateTraced(id, traced)
111+
}
112+
113+
traced match {
114+
case Some(traced) => tracedCache += id -> traced
115+
case None => tracedCache -= id
116+
}
106117
}
107118
}

codepulse/src/main/scala/com/secdec/codepulse/data/trace/slick/TreeNodeDataDao.scala

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,19 @@ private[slick] class TreeNodeDataDao(val driver: JdbcProfile) {
4848
def label = column[String]("label", O.NotNull)
4949
def kind = column[CodeTreeNodeKind]("kind", O.NotNull)
5050
def size = column[Option[Int]]("size", O.Nullable)
51-
def traced = column[Option[Boolean]]("traced", O.Nullable)
52-
def * = (id, parentId, label, kind, size, traced) <> (TreeNode.tupled, TreeNode.unapply)
51+
def * = (id, parentId, label, kind, size) <> (TreeNode.tupled, TreeNode.unapply)
5352
}
5453
val treeNodeData = TableQuery[TreeNodeData]
5554

55+
class TracedNodes(tag: Tag) extends Table[(Int, Boolean)](tag, "traced_nodes") {
56+
def nodeId = column[Int]("node_id", O.PrimaryKey, O.NotNull)
57+
def traced = column[Boolean]("traced", O.NotNull)
58+
def * = (nodeId, traced)
59+
60+
def node = foreignKey("tn_node", nodeId, treeNodeData)(_.id, onDelete = ForeignKeyAction.Cascade)
61+
}
62+
val tracedNodes = TableQuery[TracedNodes]
63+
5664
class MethodSignatureNodeMap(tag: Tag) extends Table[(String, Int)](tag, "method_signature_node_map") {
5765
def signature = column[String]("sig", O.PrimaryKey, O.NotNull)
5866
def nodeId = column[Int]("node_id", O.NotNull)
@@ -71,7 +79,7 @@ private[slick] class TreeNodeDataDao(val driver: JdbcProfile) {
7179
}
7280
val jspNodeMap = TableQuery[JspNodeMap]
7381

74-
def create(implicit session: Session) = (treeNodeData.ddl ++ methodSignatureNodeMap.ddl ++ jspNodeMap.ddl).create
82+
def create(implicit session: Session) = (treeNodeData.ddl ++ tracedNodes.ddl ++ methodSignatureNodeMap.ddl ++ jspNodeMap.ddl).create
7583

7684
def get(id: Int)(implicit session: Session): Option[TreeNode] = {
7785
(for (n <- treeNodeData if n.id === id) yield n).firstOption
@@ -134,8 +142,20 @@ private[slick] class TreeNodeDataDao(val driver: JdbcProfile) {
134142
treeNodeData ++= nodes
135143
}
136144

145+
def getTraced()(implicit session: Session) = tracedNodes.list
146+
137147
def updateTraced(id: Int, traced: Option[Boolean])(implicit session: Session) {
138-
val q = for (n <- treeNodeData if n.id === id) yield n.traced
139-
q.update(traced)
148+
traced match {
149+
case Some(traced) =>
150+
val q = for (row <- tracedNodes if row.nodeId === id) yield row.traced
151+
q.update(traced) match {
152+
case 0 => tracedNodes += id -> traced
153+
case _ =>
154+
}
155+
156+
case None =>
157+
val q = for (row <- tracedNodes if row.nodeId === id) yield row
158+
q.delete
159+
}
140160
}
141161
}

codepulse/src/main/scala/com/secdec/codepulse/tracer/TraceAPIServer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ class TraceAPIServer(manager: TraceManager) extends RestHelper with Loggable {
297297
case Full(packages) =>
298298
val ids = packages.split(',').flatMap(AsInt.unapply).toSet
299299
val tree = target.treeBuilder.projectTree(ids)
300-
TreemapDataStreamer.streamTreemapData(tree)
300+
TreemapDataStreamer.streamTreemapData(target.traceData.treeNodeData, tree)
301301

302302
case _ => BadResponse()
303303
}

codepulse/src/main/scala/com/secdec/codepulse/tracer/TraceSettingsCreator.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import com.secdec.codepulse.data.trace.TraceData
2727
object TraceSettingsCreator {
2828

2929
def generateTraceSettings(traceData: TraceData, jspMapper: Option[JspMapper]): TraceSettings = {
30-
val inclusions = traceData.treeNodeData.iterate { it =>
30+
val treeNodeData = traceData.treeNodeData
31+
import treeNodeData.ExtendedTreeNodeData
32+
33+
val inclusions = treeNodeData.iterate { it =>
3134
(for {
3235
treeNode <- it
3336
if treeNode.kind == CodeTreeNodeKind.Pkg
@@ -38,7 +41,7 @@ object TraceSettingsCreator {
3841
"^" + slashedName + "/[^/]+$"
3942
}).toList
4043
} ++ jspMapper.map { mapper =>
41-
traceData.treeNodeData.iterateJspMappings { it =>
44+
treeNodeData.iterateJspMappings { it =>
4245
(for ((jspClass, _) <- it) yield mapper getInclusion jspClass).toList
4346
}
4447
}.getOrElse(Nil)

codepulse/src/main/scala/com/secdec/codepulse/tracer/TraceUploadData.scala

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import com.secdec.codepulse.data.trace.TraceData
3434
import scala.concurrent.Future
3535
import scala.concurrent.ExecutionContext.Implicits.global
3636
import net.liftweb.common.Full
37+
import com.secdec.codepulse.data.bytecode.CodeTreeNodeKind
38+
import com.secdec.codepulse.data.trace.TreeNodeData
3739

3840
object TraceUploadData {
3941

@@ -105,7 +107,8 @@ object TraceUploadData {
105107

106108
def handleBinaryZip(file: File): TraceId = createAndLoadTraceData { traceData =>
107109
val RootGroupName = "Classes"
108-
val builder = new CodeForestBuilder(defaultTracedGroups = RootGroupName :: CodeForestBuilder.JSPGroupName :: Nil)
110+
val tracedGroups = (RootGroupName :: CodeForestBuilder.JSPGroupName :: Nil).toSet
111+
val builder = new CodeForestBuilder
109112
val methodCorrelationsBuilder = collection.mutable.Map.empty[String, Int]
110113

111114
//TODO: make this configurable somehow
@@ -139,10 +142,22 @@ object TraceUploadData {
139142
if (treemapNodes.isEmpty) {
140143
throw new NoSuchElementException("No method data found in analyzed upload file.")
141144
} else {
145+
val treeNodeData = traceData.treeNodeData
146+
import treeNodeData.ExtendedTreeNodeData
142147

143-
traceData.treeNodeData.storeNodes(treemapNodes)
144-
traceData.treeNodeData.mapJsps(jspCorrelations)
145-
traceData.treeNodeData.mapMethodSignatures(methodCorrelations)
148+
val nodeMap = treemapNodes.map(n => n.id -> n).toMap
149+
def getRoot(n: TreeNodeData): TreeNodeData = n.parentId match {
150+
case Some(parent) => getRoot(nodeMap(parent))
151+
case None => n
152+
}
153+
154+
treeNodeData.storeNodes(treemapNodes)
155+
for (n <- treemapNodes) n.traced = n.kind match {
156+
case CodeTreeNodeKind.Grp | CodeTreeNodeKind.Pkg => Some(tracedGroups contains getRoot(n).label)
157+
case _ => None
158+
}
159+
treeNodeData.mapJsps(jspCorrelations)
160+
treeNodeData.mapMethodSignatures(methodCorrelations)
146161

147162
// The `creationDate` for trace data detected in this manner
148163
// should use its default value ('now'), as this is when the data

codepulse/src/main/scala/com/secdec/codepulse/tracer/TreemapDataStreamer.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ import net.liftweb.json.Printer
3434
object TreemapDataStreamer {
3535
private val Json = new JsonFactory
3636

37-
private def writeJson(jg: JsonGenerator)(node: TreeNode) {
37+
private def writeJson(treeNodeData: TreeNodeDataAccess, jg: JsonGenerator)(node: TreeNode) {
38+
import treeNodeData.ExtendedTreeNodeData
39+
3840
jg.writeStartObject
3941

4042
jg.writeNumberField("id", node.data.id)
@@ -46,20 +48,20 @@ object TreemapDataStreamer {
4648

4749
if (!node.children.isEmpty) {
4850
jg writeArrayFieldStart "children"
49-
node.children.foreach(writeJson(jg))
51+
node.children.foreach(writeJson(treeNodeData, jg))
5052
jg.writeEndArray
5153
}
5254

5355
jg.writeEndObject
5456
}
5557

56-
def streamTreemapData(tree: List[TreeNode]): OutputStreamResponse = {
58+
def streamTreemapData(treeNodeData: TreeNodeDataAccess, tree: List[TreeNode]): OutputStreamResponse = {
5759
def writeData(out: OutputStream) {
5860
val jg = Json createGenerator out
5961

6062
try {
6163
jg.writeStartArray
62-
tree.foreach(writeJson(jg))
64+
tree.foreach(writeJson(treeNodeData, jg))
6365
jg.writeEndArray
6466
} finally jg.close
6567
}

codepulse/src/main/scala/com/secdec/codepulse/tracer/export/TraceExporter.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ object TraceExporter extends JsonHelpers {
9696
}
9797

9898
private def writeTreeNodeData(out: OutputStream, treeNodeData: TreeNodeDataAccess) {
99+
import treeNodeData.ExtendedTreeNodeData
100+
99101
streamJson(out) { jg =>
100102
jg.writeStartArray
101103

0 commit comments

Comments
 (0)