Skip to content

Commit 79e4425

Browse files
authored
Merge pull request Ericsson#806 from barnabasdomozi/parallel_mccabe
Calculate type-level McCabe metric parallelly.
2 parents d5c6671 + a8b57d6 commit 79e4425

File tree

2 files changed

+75
-73
lines changed

2 files changed

+75
-73
lines changed

plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ class CppMetricsParser : public AbstractParser
218218
static const int efferentCouplingModulesPartitionMultiplier = 5;
219219
static const int afferentCouplingModulesPartitionMultiplier = 5;
220220
static const int relationalCohesionPartitionMultiplier = 5;
221+
static const int typeMcCabePartitionMultiplier = 5;
221222
};
222223

223224
} // parser

plugins/cpp_metrics/parser/src/cppmetricsparser.cpp

Lines changed: 74 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -178,89 +178,90 @@ void CppMetricsParser::functionBumpyRoad()
178178

179179
void CppMetricsParser::typeMcCabe()
180180
{
181-
util::OdbTransaction {_ctx.db} ([&, this]
181+
using MemberT = model::CppMemberType;
182+
using AstNode = model::CppAstNode;
183+
using Entity = model::CppEntity;
184+
using AstNodeMet = model::CppAstNodeMetrics;
185+
186+
// Calculate type level McCabe metric for all types on parallel threads.
187+
parallelCalcMetric<AstNode>(
188+
"Type-level McCabe",
189+
_threadCount * typeMcCabePartitionMultiplier,// number of jobs; adjust for granularity
190+
odb::query<AstNode>::symbolType == AstNode::SymbolType::Type &&
191+
odb::query<AstNode>::astType == AstNode::AstType::Definition,
192+
[&, this](const MetricsTasks<AstNode>& tasks)
182193
{
183-
using MemberT = model::CppMemberType;
184-
using AstNode = model::CppAstNode;
185-
using Entity = model::CppEntity;
186-
using AstNodeMet = model::CppAstNodeMetrics;
187-
188-
std::map<model::CppAstNodeId, unsigned int> mcValues;
189-
190-
// Process all class definitions
191-
for (const auto& type : _ctx.db->query<AstNode>(
192-
odb::query<AstNode>::symbolType == AstNode::SymbolType::Type &&
193-
odb::query<AstNode>::astType == AstNode::AstType::Definition))
194+
util::OdbTransaction {_ctx.db} ([&, this]
194195
{
195-
// Skip if class is included from external library
196-
type.location.file.load();
197-
const auto typeFile = _ctx.db->query_one<model::File>(
198-
odb::query<model::File>::id == type.location.file->id);
199-
if (!typeFile || !cc::util::isRootedUnderAnyOf(_inputPaths, typeFile->path))
200-
continue;
201-
202-
// Skip if its a template instantiation
203-
const auto typeEntity = _ctx.db->query_one<Entity>(
204-
odb::query<Entity>::astNodeId == type.id);
205-
if (typeEntity && typeEntity->tags.find(model::Tag::TemplateInstantiation)
206-
!= typeEntity->tags.cend())
207-
continue;
208-
209-
mcValues[type.id] = 0;
210-
211-
// Process its methods
212-
for (const auto& method : _ctx.db->query<MemberT>(
213-
odb::query<MemberT>::typeHash == type.entityHash &&
214-
odb::query<MemberT>::kind == MemberT::Kind::Method))
196+
for (const AstNode& type : tasks)
215197
{
216-
// Lookup AST node of method
217-
method.memberAstNode.load();
218-
const auto methodAstNode = _ctx.db->query_one<AstNode>(
219-
odb::query<AstNode>::id == method.memberAstNode->id);
220-
if (!methodAstNode)
198+
// Skip if class is included from external library
199+
type.location.file.load();
200+
const auto typeFile = _ctx.db->query_one<model::File>(
201+
odb::query<model::File>::id == type.location.file->id);
202+
if (!typeFile || !cc::util::isRootedUnderAnyOf(_inputPaths, typeFile->path))
221203
continue;
222204

223-
// Lookup its definition (different AST node if not defined in class body)
224-
auto methodDefs = _ctx.db->query<AstNode>(
225-
odb::query<AstNode>::entityHash == methodAstNode->entityHash &&
226-
odb::query<AstNode>::symbolType == AstNode::SymbolType::Function &&
227-
odb::query<AstNode>::astType == AstNode::AstType::Definition);
228-
if (methodDefs.empty())
229-
continue;
230-
const auto methodDef = *methodDefs.begin();
231-
// Note: we cannot use query_one, because a project might have multiple
232-
// functions with the same entityHash compiled to different binaries
233-
// So we take the first result, which introduces a small level of
234-
// potential inaccuracy
235-
// This could be optimized in the future if linkage information about
236-
// translation units got added to the database
237-
238-
// Skip implicitly defined methods (constructors, operator=, etc.)
239-
const auto entity = _ctx.db->query_one<Entity>(
240-
odb::query<Entity>::astNodeId == methodDef.id);
241-
if (entity && entity->tags.find(model::Tag::Implicit) != entity->tags.cend())
205+
// Skip if its a template instantiation
206+
const auto typeEntity = _ctx.db->query_one<Entity>(
207+
odb::query<Entity>::astNodeId == type.id);
208+
if (typeEntity && typeEntity->tags.find(model::Tag::TemplateInstantiation)
209+
!= typeEntity->tags.cend())
242210
continue;
243211

244-
// Lookup metrics of this definition
245-
const auto funcMetrics = _ctx.db->query_one<AstNodeMet>(
246-
odb::query<AstNodeMet>::astNodeId == methodDef.id &&
247-
odb::query<AstNodeMet>::type == model::CppAstNodeMetrics::Type::MCCABE_FUNCTION);
248-
if (funcMetrics)
212+
unsigned int value = 0;
213+
214+
// Process its methods
215+
for (const auto& method : _ctx.db->query<MemberT>(
216+
odb::query<MemberT>::typeHash == type.entityHash &&
217+
odb::query<MemberT>::kind == MemberT::Kind::Method))
249218
{
250-
// Increase class mccabe by the method's
251-
mcValues[type.id] += funcMetrics->value;
219+
// Lookup AST node of method
220+
method.memberAstNode.load();
221+
const auto methodAstNode = _ctx.db->query_one<AstNode>(
222+
odb::query<AstNode>::id == method.memberAstNode->id);
223+
if (!methodAstNode)
224+
continue;
225+
226+
// Lookup its definition (different AST node if not defined in class body)
227+
auto methodDefs = _ctx.db->query<AstNode>(
228+
odb::query<AstNode>::entityHash == methodAstNode->entityHash &&
229+
odb::query<AstNode>::symbolType == AstNode::SymbolType::Function &&
230+
odb::query<AstNode>::astType == AstNode::AstType::Definition);
231+
if (methodDefs.empty())
232+
continue;
233+
const auto methodDef = *methodDefs.begin();
234+
// Note: we cannot use query_one, because a project might have multiple
235+
// functions with the same entityHash compiled to different binaries
236+
// So we take the first result, which introduces a small level of
237+
// potential inaccuracy
238+
// This could be optimized in the future if linkage information about
239+
// translation units got added to the database
240+
241+
// Skip implicitly defined methods (constructors, operator=, etc.)
242+
const auto entity = _ctx.db->query_one<Entity>(
243+
odb::query<Entity>::astNodeId == methodDef.id);
244+
if (entity && entity->tags.find(model::Tag::Implicit) != entity->tags.cend())
245+
continue;
246+
247+
// Lookup metrics of this definition
248+
const auto funcMetrics = _ctx.db->query_one<AstNodeMet>(
249+
odb::query<AstNodeMet>::astNodeId == methodDef.id &&
250+
odb::query<AstNodeMet>::type == model::CppAstNodeMetrics::Type::MCCABE_FUNCTION);
251+
if (funcMetrics)
252+
{
253+
// Increase class mccabe by the method's
254+
value += funcMetrics->value;
255+
}
252256
}
253-
}
254-
}
255257

256-
for (const auto& mcValue : mcValues)
257-
{
258-
model::CppAstNodeMetrics typeMcMetric;
259-
typeMcMetric.astNodeId = mcValue.first;
260-
typeMcMetric.type = model::CppAstNodeMetrics::Type::MCCABE_TYPE;
261-
typeMcMetric.value = mcValue.second;
262-
_ctx.db->persist(typeMcMetric);
263-
}
258+
model::CppAstNodeMetrics typeMcMetric;
259+
typeMcMetric.astNodeId = type.id;
260+
typeMcMetric.type = model::CppAstNodeMetrics::Type::MCCABE_TYPE;
261+
typeMcMetric.value = value;
262+
_ctx.db->persist(typeMcMetric);
263+
}
264+
});
264265
});
265266
}
266267

0 commit comments

Comments
 (0)