The more experienced here probably know a lot of this, but I found it quite interesting so I thought I'd share... https://www.owasp.org/index.php/C-Based_Toolchain_Hardening My biggest take away is the emphasis on using asserts. My professional C programming experience was only a short stint 20 years ago in a graduate position where asserts were never discussed. So I've never used asserts and it was particularly enlightening to read... "in the time it takes for you to write a printf or NSLog statement, you could have written an assert. Unlike the printf or NSLog which are often removed when no longer needed, the assert stays active forever." In relation to my own (potential) contributions to the VM, I'd be interested to learn if there are any particular aspects from that article that particularly: * resonate with current practices * areas that could be improved * are adverse to personal experience Of course there are already production, debug and assert builds. And it concurs with what I've read on the list about avoiding autotools. The problem with dependency rebuilds when CFLAGS change between production/debug builds was enlightening (if obvious in hindsight). I guess this is why each build type has its own directory. One thing I don't quite get is "Everywhere you place an if statement for validation or checking, you should have an assert. Everywhere you have an assert for validation or checking, you should have an if statement. They go hand-in-hand." Indeed, perusing asserts in the repository [1] doesn't show any nearby if statements, so maybe that statement is a stretch?? [1] https://github.com/OpenSmalltalk/opensmalltalk-vm/search?l=c&q=assert&type=Code&utf8=%E2%9C%93 cheers -ben |
Hi Ben,
On Fri, Sep 2, 2016 at 5:51 AM, Ben Coman <[hidden email]> wrote:
TL;DR, but I skimmed it. My biggest take away is the emphasis on using asserts. My +1 In relation to my own (potential) contributions to the VM, I'd be Sure. In ParcPlace I moved from just having Debug and Release builds to Debug, Assert (-O1) and Production builds cuz often the Debug build was frustratingly slow. * areas that could be improved With the appropriate setup for third-party libraries we can use Debug builds of third-party libraries with Debug and Assert VM builds, and Production builds of third-party libraries with the Production VM builds. * are adverse to personal experience No. This is good style. The code is both better commented (asserts often state important invariants) and the VM is effectively full of tests. This is at least one reason why we get away with not having micro tests for VM subcomponents, which are difficult to write since the VM is designed to consume an entire Smalltalk image and execute an entire Smalltalk system. Of course there are already production, debug and assert builds. And Good to hear! The problem with dependency rebuilds when CFLAGS change between Exactly. The builds must be kept separate; otherwise they would pollute, causing confusion and delay ;-) One thing I don't quite get is "Everywhere you place an if statement The assert scheme includes assert for use in ifs. A normal assert is an expression that produces a warning if it is false, and is compiled only in debug and assert builds. An asserts is an expression that produces a warning if it is false in debug and assert builds, but is still evaluated in all builds. So if you want an if combined with an assert you use asserta, e.g. see CoInterpreter>>callbackEnter: callbackID "Re-enter the interpreter for executing a callback" | currentCStackPointer currentCFramePointer savedReenterInterpreter wasInMachineCode calledFromMachineCode | <volatile> <export: true> <var: #currentCStackPointer type: #'void *'> <var: #currentCFramePointer type: #'void *'> <var: #callbackID type: #'sqInt *'> <var: #savedReenterInterpreter type: #'jmp_buf'> "For now, do not allow a callback unless we're in a primitiveResponse" (self asserta: primitiveFunctionPointer ~= 0) ifFalse: [^false]. self assert: primFailCode = 0. "Check if we've exceeded the callback depth" (self asserta: jmpDepth < MaxJumpBuf) ifFalse: [^false]. jmpDepth := jmpDepth + 1. ... So the asserts are eliminated in production builds and the assertas remain but do not produce a warning, so the code is equivalent to callbackEnter: callbackID "Re-enter the interpreter for executing a callback" | currentCStackPointer currentCFramePointer savedReenterInterpreter wasInMachineCode calledFromMachineCode | <volatile> <export: true> <var: #currentCStackPointer type: #'void *'> <var: #currentCFramePointer type: #'void *'> <var: #callbackID type: #'sqInt *'> <var: #savedReenterInterpreter type: #'jmp_buf'> "For now, do not allow a callback unless we're in a primitiveResponse" primitiveFunctionPointer ~= 0 ifFalse: [^false]. "Check if we've exceeded the callback depth" jmpDepth < MaxJumpBuf ifFalse: [^false]. jmpDepth := jmpDepth + 1. You'll also find eassert: which is for expensive asserts that are evaluated only if enabled. In the simulator this is controlled by the VMClass class var ExpensiveAsserts. In compiled C VMs this is controlled by the expensiveAsserts variable, which must be set by hand in lldb/gdb et al. [1] https://github.com/ _,,,^..^,,,_ best, Eliot |
Free forum by Nabble | Edit this page |