游戏是最受欢迎的软件产品之一。 这是一个巨大的行业,其中新的游戏引擎-Amazon Lumberyard。 该项目仍处于测试阶段,它有时间修复错误并提高代码质量。 引擎的开发人员在不久的将来要做很多工作,以免使数百万游戏玩家和游戏开发人员失望。
引言
Amazon Lumberyard是由Amazon开发的免费AAA跨平台游戏引擎,基于
CryEngine引擎架构,该架构于2015年获得Crytek的许可。 顺便说一句,我在
2016年
8月和
2017 年 4月已经对CryEngine进行了两次分析。 同时,我必须指出,一年之后,代码只会变得更糟。 前几天,我决定看看亚马逊基于该游戏引擎做了什么。 他们在环境方面做得很好。 开发人员和用于部署工作环境的软件的文档非常酷,而且层次很高。 但是麻烦又出在代码上! 我希望亚马逊有更多的资源来处理该项目,并且他们仍然关注代码的质量。 通过这次回顾,我希望引起开发人员对代码质量的关注,并推动在此游戏引擎的开发中寻求新方法。 代码的当前状态处于令人沮丧的状态,以至于当我查看带有分析结果的报告时,我多次更改了文章标题并重新绘制了标题图片。 图片的第一版不那么令人激动:
我们分析了最新可用版本1.14.0.1的Amazon Lumberyard的来源。 源代码来自
Github上的存储库。 Lumberyard引擎上最早的游戏之一应该是
Star Citizen 。 等待她的潜在玩家,我也邀请您看看游戏的“幕后花絮”。
与PVS-Studio集成
PVS-Studio被用作静态代码分析器。 它可用于Windows,Linux和macOS。 即 对于跨平台项目的分析,甚至还可以选择一些更舒适的工作。 除了C和C ++,还支持使用C#语言进行项目分析。
Java计划 。 世界上绝大多数代码都是用列出的语言编写的(当然,并非没有错误),因此在您的项目上尝试使用PVS-Studio分析仪,您将学到很多有趣的东西;-)。
作为Lumberyard装配系统,使用了WAF,该系统也在CryEngine中使用。 分析仪没有与该装配系统集成的特殊方法。 我决定在Windows上处理一个项目,并选择了这种开始分析的方法:
编译监视系统 。 Visual Studio的项目文件是自动生成的。 它可用于构建项目和查看分析器报告。
用于分析的命令列表如下所示:
cd /path/to/lumberyard/dev lmbr_waf.bat ... CLMonitor.exe monitor MSBuild.exe ... LumberyardSDK_vs15.sln ... CLMonitor.exe analyze --log /path/to/report.plog
正如我所说,该报告可以在Visual Studio中查看。
关于伊戈尔和高通
Amazon Lumberyard将自己定位为跨平台游戏引擎。 利用这种功能向大众推广项目很容易,但是维护起来却很困难。 PVS-Studio发出的警告之一是在代码片段上发出的,程序员Igor与Qualcomm编译器进行了对抗。 也许他解决了他的问题,但是留下了一个非常可疑的代码。 我决定用图片来画。
V523'then '语句等效于'else'语句。 toglsloperand.c 700

在此,无论计算出的条件如何,均执行相同的代码。 在留下评论的背景下,这样的决定显得可疑。
通常,项目不是唯一需要简化条件以使其清晰或纠正实际错误的地方。 这是这些地方的列表:
- V523'then'语句等效于'else'语句。 第1385章
- V523'then'语句等效于'else'语句。 tometalinstruction.c 4201
- V523'then'语句等效于'else'语句。 脚本表cpp 905
- V523'then'语句等效于'else'语句。 预算系统.cpp 701
- V523'then'语句等效于'else'语句。 editorframeworkapplication.cpp 562
- V523'then'语句等效于'else'语句。 粒子项目cpp 130
- V523'then'语句等效于'else'语句。 trackviewnodes.cpp 1223
- V523'then'语句等效于'else'语句。 propertyoarchive.cpp 447
Python ++
分析器发现了这样一个有趣的代码:
V709 CWE-682发现可疑比较:“ a == b == c”。 请记住,“ a == b == c”不等于“ a == b && b == c”。 第564章
void CallBinaryOp(....) { .... uint32_t src1SwizCount = GetNumSwizzleElements(....); uint32_t src0SwizCount = GetNumSwizzleElements(....); uint32_t dstSwizCount = GetNumSwizzleElements(....); .... if (src1SwizCount == src0SwizCount == dstSwizCount)
不幸的是,在C ++中,这样的代码可以成功编译,但看起来却根本无法执行。 C ++中的表达式按优先级求值,并在必要时执行隐式种姓。
例如,在Python语言中,这种检查是正确的。 但是在这种情况下,开发人员“陷入困境”。
另外3张控制照:
- V709 CWE-682发现可疑比较:“ a == b == c”。 请记住,“ a == b == c”不等于“ a == b && b == c”。 第654章
- V709 CWE-682发现可疑比较:“ a == b == c”。 请记住,“ a == b == c”不等于“ a == b && b == c”。 第469章
- V709 CWE-682发现可疑比较:“ a == b == c”。 请记住,“ a == b == c”不等于“ a == b && b == c”。 金属指令c 539
最好的诊断方法之一
这将与警告
V501有关
-PVS -Studio中的第一个通用诊断。 仅凭此诊断发现的错误就足以写一篇文章。 Amazon Lumberyard项目很好地证明了这一点。
不幸的是,长时间查看相同类型的错误很无聊,因此在本节中,我将仅评论几个代码片段,其余警告将被列出。
V501在'||'的左侧和右侧有相同的子表达式 运算符:hotX <0 || hotX <0编辑器utils.cpp 166
QCursor CMFCUtils::LoadCursor(....) { .... if (!pm.isNull() && (hotX < 0 || hotX < 0)) { QFile f(path); f.open(QFile::ReadOnly); QDataStream stream(&f); stream.setByteOrder(QDataStream::LittleEndian); f.read(10); quint16 x; stream >> x; hotX = x; stream >> x; hotY = x; } .... }
条件缺少变量
hotY。 经典错字。
V501在'&&'运算符的左侧和右侧有相同的子表达式'sp.m_pTexture == m_pTexture'。 shadercomponents.h 487
V501在'&&'运算符的左侧和右侧有相同的子表达式'sp.m_eCGTextureType == m_eCGTextureType'。 shadercomponents.h 487
bool operator != (const SCGTexture& sp) const { if (sp.m_RegisterOffset == m_RegisterOffset && sp.m_Name == m_Name && sp.m_pTexture == m_pTexture &&
一次在此片段中发现了两个复制粘贴。 为了清楚起见,我画了箭头。
V501在'=='运算符的左侧和右侧有相同的子表达式:pSrc.GetLen()== pSrc.GetLen()fbxpropertytypes.h 978
inline bool FbxTypeCopy(FbxBlob& pDst, const FbxString& pSrc) { bool lCastable = pSrc.GetLen() == pSrc.GetLen(); FBX_ASSERT( lCastable ); if( lCastable ) pDst.Assign(pSrc.Buffer(), (int)pSrc.GetLen()); return lCastable; }
在这里,我想向
AUTODESK的开发人员问好。 此错误来自其
FBX SDK库。 混淆变量
pSrc和
pDst 。 我认为,除了Lumberyard之外,还有许多其他用户的项目依赖于此错误的代码。
V501 '&&'运算符的左侧和右侧都有相同的子表达式:pTS-> pRT_ALD_1 && pTS-> pRT_ALD_1 d3d_svo.cpp 857
void CSvoRenderer::ConeTracePass(SSvoTargetsSet* pTS) { .... if (pTS->pRT_ALD_1 && pTS->pRT_ALD_1) { static int nPrevWidth = 0; if (....) { .... } else { pTS->pRT_ALD_1->Apply(10, m_nTexStateLinear); pTS->pRT_RGB_1->Apply(11, m_nTexStateLinear); } } .... }
返回Lumberyard代码。 在这种情况下,检查相同的指针
pTS-> pRT_ALD_1 。 其中之一应该是
pTS-> pRT_RGB_1 。 也许即使在解释之后,也无法立即看到差异,但有一个差异:差异在于短子串
ALD和
RGB 。 如果系统提示您手动进行代码审查就足够了,请显示此示例。
如果这个例子还不够,那么还有5个类似的例子。
- V501在'||'的左侧和右侧有相同的子表达式 运算符:!pTS-> pRT_ALD_0 ||!pTS-> pRT_ALD_0 d3d_svo.cpp 1041
- V501'&&'运算符的左侧和右侧有相同的子表达式:m_pRT_AIR_MIN && m_pRT_AIR_MIN d3d_svo.cpp 1808
- V501'&&'运算符的左侧和右侧有相同的子表达式:m_pRT_AIR_MAX && m_pRT_AIR_MAX d3d_svo.cpp 1819
- V501'&&'运算符的左侧和右侧有相同的子表达式:m_pRT_AIR_SHAD && m_pRT_AIR_SHAD d3d_svo.cpp 1830
- V501'&&'运算符的左侧和右侧有相同的子表达式:s_pPropertiesPanel && s_pPropertiesPanelentityobject.cpp 1700
正如我所承诺的,这是其余
V501警告的列表,没有代码示例:
展开清单- V501在“ ||”的左侧和右侧有相同的子表达式“ MaxX <0” 操作员。 czbufferculler.h 128
- V501有相同的子表达式'm_joints [op [1]]。在'-'运算符的左侧和右侧限制[1] [i]'。 第795章
- V501在'-'运算符的左侧和右侧有相同的子表达式'm_joints [i] .limits [1] [j]'。 第2044章
- V501在'|'的左侧和右侧有相同的子表达式'irect [0] .x +1-irect [1] .x >> 31' 操作员。 trimesh.cpp 4029
- V501在“ ||”的左侧和右侧有相同的子表达式“ b-> mlen <= 0” 操作员。 bstrlib.c 1779
- V501在“ ||”的左侧和右侧有相同的子表达式“ b-> mlen <= 0” 操作员。 bstrlib.c 1827
- V501在“ ||”的左侧和右侧有相同的子表达式“ b-> mlen <= 0” 操作员。 bstrlib.c 1865
- V501在“ ||”的左侧和右侧有相同的子表达式“ b-> mlen <= 0” 操作员。 bstrlib.c 1779
- V501在“ ||”的左侧和右侧有相同的子表达式“ b-> mlen <= 0” 操作员。 bstrlib.c 1827
- V501在“ ||”的左侧和右侧有相同的子表达式“ b-> mlen <= 0” 操作员。 bstrlib.c 1865
- V501'-'运算符的左侧和右侧有相同的子表达式:dd-dd finalizingspline.h 669
- V501在'^'运算符的左侧和右侧有相同的子表达式'pVerts [2]-pVerts [3]'。 roadrendernode.cpp 307
- V501在“ ||”的左侧和右侧有相同的子表达式“!PGroup-> GetStatObj()”。 操作员。 terrain_node.cpp 594
- V501在'||'的左侧和右侧有相同的子表达式 运算符:val == 0 || val ==-0 xmlcpb_attrwriter.cpp 367
- V501在“ |”的左侧和右侧有相同的子表达式“ geom_colltype_solid” 操作员。 附件管理器.cpp 1058
- V501在“ |”的左侧和右侧有相同的子表达式“(TriMiddle-RMWPosition)” 操作员。 modelmesh.cpp 174
- V501在'|'的左边和右边有相同的子表达式'(goal-pAbsPose [b3] .t)'。 操作员。 posemodifierhelper.cpp 115
- V501在'|'的左侧和右侧有相同的子表达式'(goal-pAbsPose [b4] .t)'。 操作员。 posemodifierhelper.cpp 242
- V501在“ ||”的左侧和右侧有相同的子表达式“(m_eTFSrc == eTF_BC6UH)”。 操作员。 第983章
- V501'-'运算符左右两侧有相同的子表达式:q2.vz-q2.vz azentitynode.cpp 102
- V501'-'运算符的左侧和右侧有相同的子表达式:q2.vz-q2.vz entitynode.cpp 107
- V501在'||'的左侧和右侧有相同的子表达式'm_listRect.contains(event-> pos())'。 操作员。 aidebuggerview.cpp 463
- V501'&&'运算符的左侧和右侧有相同的子表达式:pObj-> GetParent()&& pObj-> GetParent()designerpanel.cpp 253
关于相机在游戏中的位置
PVS-Studio-
V502中的诊断速度第二快。 该诊断比某些新的编程语言要早,在新的编程语言中,不再会出现这种错误。 对于C ++,此错误可能是相关的,也许总是如此。
让我们从一个简单的例子开始。
V502也许'?:'运算符的工作方式与预期的不同。 '?:'运算符的优先级低于'+'运算符。 zipencryptor.cpp 217
bool ZipEncryptor::ParseKey(....) { .... size_t pos = i * 2 + (v1 == 0xff) ? 1 : 2; RCLogError("....", pos); return false; .... }
加法运算的优先级高于三元运算符。 因此,该表达式的计算逻辑与作者假设的完全不同。
您可以通过以下方式解决错误:
size_t pos = i * 2 + (v1 == 0xff ? 1 : 2);
V502也许'?:'运算符的工作方式与预期的不同。 '?:'运算符的优先级低于'-'运算符。 3dengine.cpp 1898
float C3DEngine::GetDistanceToSectorWithWater() { .... return (bCameraInTerrainBounds && (m_pTerrain && m_pTerrain->GetDistanceToSectorWithWater() > 0.1f)) ? m_pTerrain->GetDistanceToSectorWithWater() : max(camPostion.z - OceanToggle::IsActive() ? OceanRequest::GetOceanLevel() : GetWaterLevel(), 0.1f); }
这是一个示例代码,它们可用于相机位置。 一个例子很难用眼睛察觉,并且有一个错误。 为了发布,代码的格式已更改,但是在源文件中,此代码甚至更不可读。
该子字符串中存在错误:
camPostion.z - OceanToggle::IsActive() ? .... : ....
如果游戏中的摄像头突然开始出现异常行为,那么您应该知道开发人员保存了静态代码分析:D。
带有类似警告的其他示例:
- V502也许'?:'运算符的工作方式与预期的不同。 '?:'运算符的优先级低于'-'运算符。 scriptbind_ai.cpp 5203
- V502也许'?:'运算符的工作方式与预期的不同。 '?:'运算符的优先级低于'+'运算符。 第136章
- V502也许'?:'运算符的工作方式与预期的不同。 '?:'运算符的优先级低于'&&'运算符。 shapetool.h 98
CryEngine旧版
Amazon Lumberyard基于
CryEngine代码。 而不是最佳版本。 通过查看分析仪报告,我得出了这样的结论。 在我的两次代码审查中,发现的一些错误已在最新版本的CryEngine中修复,但仍然存在于Lumberyard代码中。 另外,在过去的一年中,分析器得到了显着改进,并且有可能发现两个游戏引擎中现在存在的其他错误。 但是有了Lumberyard,情况变得更糟。 亚马逊基本上继承了CryEngine的所有技术债务。 但是,他自己的技术职责当然会在每个公司中自己出现:)。
在本节中,我将为您提供一些在最新版本的CryEngine中修复的错误,现在仅是Lumberyard项目的问题。
V519'BlendFactor [2]'变量连续两次被赋值。 也许这是一个错误。 检查行:1283,1284。ccrydxgldevicecontext.cpp 1284
当Lumberyard开发人员发现此错误仅存在于他们身上时,大约会遇到这种情绪。
顺便说一句,还有两个:
- V519连续两次给'm_auBlendFactor [2]'变量赋值两次。 也许这是一个错误。 检查行:919、920。ccrydxgldevicecontext.cpp 920
- V519连续两次给'm_auBlendFactor [2]'变量赋值两次。 也许这是一个错误。 检查行:926、927。ccrydxgldevicecontext.cpp 927
出现这样的错误:
V546类的成员由其自身初始化:'eConfigMax(eConfigMax.VeryHigh)'。 1837年
ParticleParams() : .... fSphericalApproximation(1.f), fVolumeThickness(1.0f), fSoundFXParam(1.f), eConfigMax(eConfigMax.VeryHigh),
在CryEngine中,通常重写了该类,但此处仍然存在初始化错误。
V521使用','运算符的此类表达式很危险。 确保表达式“!SWords [iWord] .empty(),iWord ++”正确。 tacticalpointsystem.cpp 3376
bool CTacticalPointSystem::Parse(....) const { string sInput(sSpec); const int MAXWORDS = 8; string sWords[MAXWORDS]; int iC = 0, iWord = 0; for (; iWord < MAXWORDS; !sWords[iWord].empty(), iWord++) { sWords[iWord] = sInput.Tokenize("_", iC); } .... }
CryEngine也已经重写了可疑循环。
错误的寿命比您想象的更长
对于第一次开始使用PVS-Studio的用户,情况大致相同:他们发现一个错误,发现它是几个月前添加的,并且很高兴地意识到他们奇迹般地避免了用户中问题的出现。 经历了这样的故事之后,我们的许多客户开始定期使用PVS-Studio。
有时,为了启动代码质量控制流程,公司必须多次访问这种情况。 这是有关CryEngine和Lumberyard的示例:
V557 CWE-119阵列可能超限。 'id'索引指向数组边界之外。 gameobjectsystem.cpp 113
uint32 CGameObjectSystem::GetExtensionSerializationPriority(....) { if (id > m_extensionInfo.size()) { return 0xffffffff;
如您所知,Amazon Lumberyard不是基于最新版本的CryEngine。 但是,借助PVS-Studio分析仪,有可能发现两个游戏引擎中当前存在的错误。 有必要使用运算符'> ='检查索引...
索引错误很严重。 而且,有
六个这样的地方
! 这是另一个示例:
V557 CWE-119阵列可能超限。 “索引”索引指向数组边界之外。 carseatgroup.cpp 73
CVehicleSeat* CVehicleSeatGroup::GetSeatByIndex(unsigned index) { if (index >= 0 && index <= m_seats.size()) { return m_seats[index]; } return NULL; }
有人犯了相同类型的错误,而这些错误并没有得到解决,因为它们一次也没有被包括在CryEngine Bug评论中。
剩余警告:
- V557 CWE-119阵列可能超限。 'id'索引指向数组边界之外。 gameobjectsystem.cpp 195
- V557 CWE-119阵列可能超限。 'id'索引指向数组边界之外。 gameobjectsystem.cpp 290
- V557 CWE-119阵列可能超限。 'stateId'索引指向数组边界之外。 车辆动画cpp 311
- V557 CWE-119阵列可能超限。 'stateId'索引指向数组边界之外。 车辆动画cpp 354
代码中长期存在的错误只能通过相应级别的项目测试来解释。 据信,静态分析只能在未使用的代码中发现错误。 因此,事实并非如此。 开发人员忘记了,大多数用户静默地遭受程序中不明显的错误,而这些非常罕见的错误的出现通常会对整个公司的工作,声誉及其销售(如果有)产生不利影响。
复制粘贴编程的不同阴影
当您到达本文的这一部分时,您可能会注意到复制粘贴编程是造成许多问题的原因。 在PVS-Studio中,通过各种诊断来实现对此类错误的搜索。 本节将提供
V561附带的复制粘贴
示例 。
这是在重叠范围中声明具有相同名称的变量时的可疑代码示例。
V561 CWE-563将值分配给'pLibrary'变量可能比重新声明它更好。 先前的声明:entityobject.cpp,第4703行。Entityobject.cpp4706
void CEntityObject::OnMenuConvertToPrefab() { .... IDataBaseLibrary* pLibrary = GetIEditor()->Get....; if (pLibrary == NULL) { IDataBaseLibrary* pLibrary = GetIEditor()->Get....; } if (pLibrary == NULL) { QString sError = tr(....); CryMessageBox(....); return; } .... }
pLibrary指针不会按预期覆盖。 该指针的初始化在条件和类型声明的情况下被完全复制。
我将列出所有类似的地方:
- V561 CWE-563将值分配给'eType'变量可能比重新声明它更好。 先前的声明:toglsloperand.c,第838行。toglsloperand.c 1224
- V561 CWE-563将值分配给'eType'变量可能比重新声明它更好。 先前的声明:toglsloperand.c,第838行。toglsloperand.c 1305
- V561 CWE-563最好给'rSkelPose'变量赋值,而不是重新声明它。 先前的声明:attachmentmanager.cpp,第409行。attachmentmanager.cpp458
- V561 CWE-563将值分配给'nThreadID'变量可能比重新声明它更好。 先前的声明:d3dmeshbaker.cpp,第797行。d3dmeshbaker.cpp 867
- V561 CWE-563将值分配给'directoryNameList'变量可能比重新声明它更好。 先前的声明:assetimportermanager.cpp,第720行。assetimportermanager.cpp 728
- V561 CWE-563将值分配给'pNode'变量可能比重新声明它更好。 先前的声明:breakpointsctrl.cpp,第340行。breakpointsctrl.cpp 349
- V561 CWE-563将值分配给'pLibrary'变量可能比重新声明它更好。 先前的声明:prefabobject.cpp,行1443。prefabobject.cpp 1446
- V561 CWE-563将值分配给'pLibrary'变量可能比重新声明它更好。 先前的声明:prefabobject.cpp,第1470行。prefabobject.cpp 1473
- V561 CWE-563最好给'cmdLine'变量赋值而不是重新声明。 先前的声明:fileutil.cpp,第110行。fileutil.cpp 130
- V561 CWE-563将值赋给'sfunctionArgs'变量可能比重新声明它更好。 先前的声明:attributeitemlogiccallbacks.cpp,第291行。attributeitemlogiccallbacks.cpp 303
- V561 CWE-563将值赋给'curveName'变量可能比重新声明它更好。 先前的声明:qgradientselectorwidget.cpp,第475行。qgradientselectorwidget.cpp 488
一长串...列出的一些地方甚至是所描述示例的完整副本。
特征值初始化
在游戏引擎的代码中,发现了很多将变量分配给自身的地方。 这些代码留给了调试的地方,代码只是经过精心设计的地方(它通常也是错误的来源),因此,我将为您提供一段对我来说最可疑的代码。
V570将'behaviorParams.ignoreOnVehicleDestroyed'变量分配给它自己。 vehiclecomponent.cpp 168
bool CVehicleComponent::Init(....) { .... if (!damageBehaviorTable.getAttr(....) { behaviorParams.ignoreOnVehicleDestroyed = false; } else { behaviorParams.ignoreOnVehicleDestroyed =
在当前视图中,根本不需要
else分支。 但是也许这段代码包含一个错误:他们想为变量赋相反的值:
bValue = !bValue
但是,开发人员最好熟悉此诊断的结果。
问题处理
本节将提供许多示例,说明在错误处理期间出现问题时。
例子1V606无主令牌'nullptr'。 dx12rootsignature.cpp 599
RootSignature* RootSignatureCache::AcquireRootSignature(....) { .... RootSignature* result = new RootSignature(m_pDevice); if (!result->Init(params)) { DX12_ERROR("Could not create root signature!"); nullptr; } m_RootSignatureMap[hash] = result; return result; } }
忘记写
返回nullptr; 。 现在,
结果变量的无效值将在代码的其他地方使用。
完全相同的代码被复制到另一个地方:
- V606无主令牌'nullptr'。 dx12rootsignature.cpp 621
例子2V606无主令牌'false'。 fillspacetool.cpp 191
bool FillSpaceTool::FillHoleBasedOnSelectedElements() { .... if (validEdgeList.size() == 2) { .... } if (validEdgeList.empty()) { .... for (int i = 0, iVertexSize(....); i < iVertexSize; ++i) { validEdgeList.push_back(....); } } if (validEdgeList.empty())
缺少
return语句的错误的非常有趣的例子。 现在可以访问一个空容器来访问索引了。
例子3V564 CWE-480'&'运算符应用于布尔类型值。 您可能忘记了括号或打算使用'&&'运算符。 2914年
void SetDataTypes(....) { ....
错误地检查了标志中是否存在位。 否定运算符应用于标志值,而不是整个表达式。 正确地这样写:
if(!(psContext->flags & ....))
更多类似的警告:
- V564 CWE-480'|' 运算符应用于布尔类型值。 您可能忘记了括号或打算使用'||' 操作员。 d3dhwshader.cpp 1832
- V564 CWE-480'&'运算符应用于布尔类型值。 您可能忘记了括号或打算使用'&&'运算符。 trackviewdialog.cpp 2112
- V564 CWE-480'|' 运算符应用于布尔类型值。 您可能忘记了括号或打算使用'||' 操作员。 imagecompiler.cpp 1039
例子4V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1491
static std::vector<std::string> PyGetPrefabLibrarys() { CPrefabManager* pPrefabManager = GetIEditor()->GetPrefabMa....; if (!pPrefabManager) { std::runtime_error("Invalid Prefab Manager."); } .... }
引发异常时出错。 有必要这样写:
throw std::runtime_error("Invalid Prefab Manager.");
此类错误的完整列表:
- V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1515
- V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1521
- V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1543
- V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1549
- V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1603
- V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1619
- V596 CWE-390已创建对象,但未使用该对象。 可能没有'throw'关键字:throw runtime_error(FOO); prefabobject.cpp 1644
使用内存时的几个问题
V549 CWE-
688'memcmp '函数的第一个参数等于第二个参数。 meshutils.h 894
struct VertexLess { .... bool operator()(int a, int b) const { .... if (m.m_links[a].links.size() != m.m_links[b].links.size()) { res = (m.m_links[a].links.size() < m.m_links[b].links.size()) ? -1 : +1; } else { res = memcmp(&m.m_links[a].links[0], &m.m_links[a].links[0], sizeof(m.m_links[a].links[0]) * m.m_links[a].links.size()); } .... } .... };
该条件比较两个向量的大小。如果它们相等,那么在else分支中,使用memcmp()函数比较向量的第一元素的值。但是此函数的第一个和第二个参数相同!访问数组元素相当麻烦。有索引a和b。他们中最有可能出现错字。V611 CWE-762使用“ new T []”运算符分配了内存,但使用“ delete”运算符释放了内存。考虑检查此代码。最好使用'delete [] data;'。向量n.h 102 ~vectorn_tpl() { if (!(flags & mtx_foreign_data)) { delete[] data; } } vectorn_tpl& operator=(const vectorn_tpl<ftype>& src) { if (src.len != len && !(flags & mtx_foreign_data)) { delete data;
数据指针存储器正在使用无效的语句释放。delete []运算符必须在所有地方使用。无法访问的代码
V779 CWE-561检测不到代码。可能存在错误。fbxskinimporter.cpp 67 Events::ProcessingResult FbxSkinImporter::ImportSkin(....) { .... if (BuildSceneMeshFromFbxMesh(....) { context.m_createdData.push_back(std::move(createdData)); return Events::ProcessingResult::Success;
条件语句的所有分支以函数的退出结束。但是,某些代码未执行。V779 CWE-561检测不到代码。可能存在错误。第153章 bool DockableLibraryTreeView::Init(IDataBaseLibrary* lib) { .... if (m_treeView && m_titleBar && m_defaultView) { if (m_treeView->topLevelItemCount() > 0) { ShowTreeView(); } else { ShowDefaultView(); } return true;
在这段代码中很容易发现错误。但是,如果您长时间编写代码,则注意力会急剧下降,并且此类错误很容易落入项目中。V622 CWE-478考虑检查“ switch”语句。第一个“ case”运算符可能会丢失。datum.cpp 872 AZ_INLINE bool IsDataGreaterEqual(....) { switch (type.GetType()) { AZ_Error("ScriptCanvas", false, "...."); return false; case Data::eType::Number: return IsDataGreaterEqual<Data::NumberType>(lhs, rhs); .... case Data::eType::AABB: AZ_Error("ScriptCanvas", false, "....", Data::Traits<Data::AABBType>::GetName()); return false; case Data::eType::OBB: AZ_Error("ScriptCanvas", false, "....", Data::Traits<Data::OBBType>::GetName()); return false; .... }
如果开关包含在case / default语句之外的代码,则永远不会执行它。结论
本文包括95个分析器警告,其中25个带有代码示例。整个分析仪报告中有多少材料?我迅速滚动了高级警报的三分之一。还有“中”和“低”,一组用于搜索优化的诊断程序以及分析仪的其他尚未开发的机会-这些是数百个更明显的错误和数千个未开发的警告。然后,读者需要问自己一个问题:“这种方法是否可以发布一个好的游戏引擎?”没有代码质量控制。具有旧错误的CryEngine代码被作为基础,添加了新错误。只有在下一次检查代码之后,CryEngine本身才能最终确定。亚马逊拥有一切机会利用其资源朝着代码质量的方向努力,并发布最酷的游戏引擎!不要很沮丧。在PVS-Studio客户中,还有三十多家参与游戏的公司。通过选择过滤器“游戏开发”,您可以在我们的网站“ 客户 ” 页面上了解他们及其产品。因此,我们正在逐步改善世界。也许我们可以改善Amazon Lumberyard :)。一位同事最近写了一篇有关游戏软件质量的文章,建议您阅读:“ 视频游戏行业的静态分析:十大软件错误”。链接下载PVS-Studio分析仪,没有它怎么办;-)
如果您想与说英语的读者分享这篇文章,请使用以下链接:Svyatoslav Razmyslov。亚马逊伐木场:痛苦的尖叫