coverity multi-threaded whitepaper

Upload: javierlorcahernando

Post on 06-Apr-2018

228 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    1/13

    Ensuring Code

    Quality inMulti-threaded

    ApplicationsHow to Eliminate Concurrency Deectswith Static Analysis

    Ben Chel, CTO

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    2/13

    Ensuring Code Quality in Multi-threaded Applications

    Introduction

    Most developers would agree that consumers o sotware today continually demand more rom their applications.

    Because o its pace o evolution to date, the world now anticipates a seemingly endless expansion o capabilities

    rom their sotware, regardless o where that sotware is applied. For the last 40 years o computing, Moores

    Law has held true, with the number o transistors available in an integrated circuit doubling approximately

    every two years. This constant pace o innovation provided sotware developers with critical hardware resources,

    enabling them to expand the eatures and unctionalities o their applications without concern or perormance.

    However, while Moores Law still is holding steady, single-core processing has reached its perormance ceiling.

    In response, hardware manuacturers have developed new multi-core (or multi-processor) devices to accomplish

    the speed gains to which the world had grown accustomed.

    Today, the world o sotware development is presented with a new challenge. To ully leverage this new class o

    multi-core hardware, sotware developers must change the way they create applications. By turning their ocus

    to multi-threaded applications, developers will be able to take ull advantage o multi-core devices and deliver

    sotware that meets the demands o the world. But this paradigm o multi-threaded sotware development adds

    a new wrinkle o complexity or those who care the utmost about sotware quality. Concurrency deects such as

    race conditions and deadlocks are sotware deect types that are unique to multi-threaded applications. Complex

    and hard-to-ind, these deects can quickly derail a sotware project. To avoid catastrophic ailures in multi-

    threaded applications, sotware development organizations must understand how to identiy and eliminate these

    deadly problems early in the application development liecycle.

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    3/13 Ensuring Code Quality in Multi-threaded Applications

    Most sotware development over time has been predicated on single-core

    hardware. Thereore, the collective knowledge o sotware developers across

    universities, companies, organizations and countries has been based primarily

    on single processor hardware platorms. Despite the act that multi-processor

    systems have existed or a long time, they have not been the ocus o general

    purpose sotware development. Instead, early multi-processor hardware was

    preserved or computationally-intensive applications. As a result, multi-

    threaded sotware development has not been much o a ocus in mainstreamsotware development.

    Now that the world is moving to multi-core on nearly everything, every

    sotware developer must become versed in multi-threaded development. There

    are armies o developers that are now challenged with learning how to take

    advantage o multi-core on the ly, because they have no choice in delivering

    on the demands o aster sotware release ater release.

    The bad news or sotware developers is that multi-threaded applications

    introduce a host o concurrency-related errors, which historically, have been

    impossible to eectively test or. When they occur in the ield, concurrency

    deects can be catastrophic or sotware, requiring days, i not weeks or months,

    to diagnose and repair.

    Developers responsible or creating high reliability applications, such as those

    in the embedded space, inherit this dangerous new blind spot regarding code quality when they move rom

    single- to multi-threaded applications. This paper will review the most common pitalls that sotware developersace when creating multi-threaded applications, and how innovative new sotware analysis capabilities are

    emerging that will help development organizations capitalize on the exciting new world o multi-core hardware.

    Moving to Multi-threadedApplication Development

    Did you usemulti-core today?

    The most advertised use o multi-

    core processing has been in con-

    sumer markets on devices ranging

    rom PCs that leverage processors

    rom Intel or AMD to gaming

    consoles such as Sonys Play

    Station 3. In these markets, the

    number o processors is a clear

    competitive advantage to which

    consumers are becoming attuned.

    In enterprise markets, multi-core

    technology has been adopted

    in a number o processor types

    including those used in the

    digital signal, network and

    embedded markets.

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    4/133 Ensuring Code Quality in Multi-threaded Applications

    To take advantage o multi-core hardware, sotware developers are required

    to create multi-threaded applications that can execute multiple operations

    concurrently. Concurrency creates new complexities in the sotware

    development process that can introduce hard-to-ind, crash-causing sotware

    deects. Because widespread

    multi-threaded development is still in its relative inancy, many o todays

    existing deect detection tools are

    ill prepared to help identiy and eliminate this new class o deects.

    Testing sotware code is notoriously diicult, even or single-threaded

    applications. One reason this is a challenge, is that properly testing an

    application requires the simulation o all possible inputs to the program.

    Many organizations are satisied when they achieve 95% line coverage in

    their programs (meaning 95% o all lines o code in their program are executed

    at least once in their test suite). However, this metric o coverage does not even

    come close to the total number o possible execution paths through a code base.

    Each decision point in a program means at least a actor o two in the number

    o paths though that program.

    Multi-threaded applications add a new level o complexity to the program that

    results in an exponential increase in the number o possible run time scenarios.

    See Figure below or an example as to how turning a simple single-threaded

    application into a multi-threaded application increases the complexity

    tremendously.

    CommonCurrency DeectsRace Condition:

    Multiple threads access the

    same shared data without the

    appropriate locks to protect access

    points. When this deect occurs,

    one thread may inadvertently

    overwrite data used by another

    thread, leading to both loss o

    inormation and data corruption.

    Thread Block:

    A thread calls a long-running

    operation while holding a lock

    thereby preventing the progress

    o other threads. When this

    deect occurs, application

    perormance can drop

    dramatically due to a single

    bottleneck or all threads.

    Deadlock:

    Two or more threads wait or

    locks in a circular chain such that

    the locks can never be acquired.

    When this deect occurs, the

    entire sotware system may halt,

    as none o the threads can either

    proceed along their current

    execution paths or exit.

    Why Concurrency (Multi-core)Deects Are So Dangerous

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    5/134 Ensuring Code Quality in Multi-threaded Applications

    Figure 1: Simple single-threaded program

    To completely test the program in Figure , the sotware developer would simply need to make sure that they

    ed the program a value that was less than 00 as well as a value that was greater than or equal to 00. Then thetwo paths through the program would be explored and the program would be tested entirely. (NOTE: This

    assumes that nothing interesting happens in the input_from_user functionadmittedly a bad assumption in

    sotware development!). Now imagine that the end-user wanted this program to input two values instead o one

    with the same logic ater the input. This change would increase the complexity o the single-threaded application

    in such a way that the sotware developer would need to explore our dierent paths to ully test the program.

    Now imagine that a requirement comes in rom the ield to make this application multi-threaded to take

    advantage o a dual-core processor. This change would require that the code in Figure would be executed

    simultaneously by two dierent threads to take in the two inputs. Assuming that each statement is made up o

    three instructions (likely an underestimate), the number o possible interleavings o the instructions in the two

    threads leads to a mind boggling 94,480 dierent execution possibilities.

    By moving to multi-threaded code in this simpliied example, complexity explodes by ive orders o magnitude,

    creating a tremendous challenge or any tester o sotware. I this type o deect does happen to slip into the

    ield, developers are then aced with a dramatic increase in the number o avenues that require exploration

    beore they can determine the root cause o the problem. Worse, or the irst time, developers may not be able

    to reproduce problems that occur in multi-threaded applications because they are not in control o how theirapplication will be executed when it runs. The operating system and thread scheduler together decide when

    each thread will be executed as multiple threads execute.

    Fortunately new technology in Coverity Prevent is helping developers avoid costly concurrency deects. Leveraging

    breakthroughs in static analysis, Coveritys solution can analyze code priorto run-time enabling developers to

    identiy and eliminate onerous multi-threaded deects including race conditions, thread blocks and deadlocks.

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    6/135 Ensuring Code Quality in Multi-threaded Applications

    Deadlocks (situations where a multi-threaded program cannot make any progress) are a particularly diicult

    concurrency issue to debug. They arise when the order o lock acquisition used by the program is inconsistent.

    A simple example is lock inversion, which happens when one thread attempts to acquire a lock(a), then holds

    on to the lock while attempting to acquire lock(b) . I another thread tries to acquire the locks in the opposite

    order (acquiring b beore a), neither thread can make progress, and the program is deadlocked. A sample o

    this type o sotware deect can be ound in Figure . (Note: While the remaining code examples in this paper

    are in Java, all the deects discussed herein can occur in any programming language. Coverity Prevent discovers

    concurrency deects in C, C++, and Java code.)

    Figure 2: Deadlock Defect in Java Code

    I two distinct threads call methods lock1 and lock2, an unlucky scheduling might have the irst thread acquire

    lock(a), while the second thread acquires lock(b). In this state, it is impossible or either thread to acquire the

    lock it needs to continue execution or release the lock it holds. More complicated deadlocks exist that involve

    the acquisition o multiple locks across multiple threads.

    Deadlocks

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    7/13 Ensuring Code Quality in Multi-threaded Applications

    One way that advanced static analysis technology detects deadlocks is to ensure that all locks in the program

    are acquired and released in a consistent order. More concretely, the static analysis can construct an acyclic lock

    graph or a program. Nodes in a lock graph represent lock names, and an edge between nodes (a) and (b) denotes

    that at some point, lock(a) was held while lock(b) was acquired.

    I a lock graph contains a cycle, the program may have a lock ordering or lock inversion deadlock. Innovative

    static analysis is now capable o inding these lock ordering deects inter-procedurally (through method calls),

    as these are likely to be missed by conventional code review or other deect detection techniques.

    In Figure above, Coverity Prevent would create the ollowing lock graph or method lock1:

    The edge rom Lock.a to Lock.b indicates that Lock.a should always be acquired beore Lock.b. Analysis o

    method lock2, however, causes the analysis to add an edge rom Lock.b to Lock.a:

    This violates the requirement that the lock graph be acyclic. Thereore, a deect would be reported in lock2.

    One o the biggest diiculties in leveraging static analysis to detect deadlocks is the issue o naming locks. In

    Figure , it is clear that synchronized(a) locks the static ield a declared in the Lock class. But naming is

    not always so obvious. Consider the ollowing reactoring olock2.

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    8/13 Ensuring Code Quality in Multi-threaded Applications

    Figure 3: Refactoring lock2

    Coverity Prevent is sophisticated enough to determine that the call to method (f) has the same eect as beore.It should attempt to add an edge rom Lock.b to Lock.a, ail, and report a deect. The details behind the lock

    naming algorithm are part o the innovation that makes Coverity Prevent so powerul at inding complex,

    inter-procedural deadlock deects in C, C++ and Java code.

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    9/138 Ensuring Code Quality in Multi-threaded Applications

    Race Conditions

    Race conditions are another potentially disastrous type o concurrency deect that is endemic to multi-threaded

    applications. A race condition occurs when multiple threads access the same piece o data concurrently, at least

    one thread accessing the data is writing to the data (as opposed to simply reading the data), and the execution

    depends critically on the relative timing o events.

    This is as concrete a deinition as can be achieved in languages like C and C++ that do not have a ormally

    deined memory model. In Java, a more precise (but not necessarily equivalent) deinition can be ormulated

    based on the Java memory model. A data race occurs when a variable is read by one or more threads and written

    by at least one thread, where the read and write events are not ordered by the happens-before relationship.

    The happens-beore relationship precisely deines when events are made visible to other threads (check the JavaLanguage Speciication or a complete deinition o happens-beore). See Figure 4 below or an example o this.

    Figure 4: Race Condition

    Suppose that an instance o class Race is used to keep a counter o active events across multiple threads. One

    problem with Race is that the ++ operator is not atomic. Iincrement is called 0 times rom two dierent

    threads, the inal value ocount could be less than 0, because some o the writes to count can be clobberedby writes rom the second thread. One possible ix is to synchronize the increment method as shown in

    Figure 5 below.

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    10/139 Ensuring Code Quality in Multi-threaded Applications

    Figure 5: Race Condition with Synchronized Increment Method

    With count accessed in a synchronized block, interleaved calls to increment on the same instance object are

    eliminated. Coverity Prevent helps programmers avoid race conditions by inerring and enorcing guarded-by

    relationships on shared ields in the program. A ield (f) is guarded-by lock(l) i accesses to (f) must be protected

    by holding lock(l). In Figure 5, the count variable is guarded-by the lockield o the Race class. The set o

    guarded-by relationships essentially deine the locking discipline or each class in the program.

    Coverity Prevent is the irst static analysis solution to iner guarded-by relationships. When a preponderance

    o accesses to a ield occur with the same lock held, a guarded-by relationship is inerred between the accessed

    ield and the held lock. This may sound simple, but a number o sophisticated techniques are necessary to make

    this approach tenable.

    Most importantly, the same lock-naming algorithm used in the context o deadlock detection is applied to

    associate ields with the speciic lock used to protect access. This serves two purposes. First, it yields stronger

    correlations, since ields accessed incidentally with dierent locks held are not used to iner guarded-by

    relationships. Second, it allows Coverity Prevent to suggest a possible way to correct the discovered deect, such

    as enclosing the access in a synchronized scope with the suggested lock.

    Another important technique in discovering race conditions with static analysis is to iner inormation about theprograms thread execution model. This includes inormation such as where threads are created, which unctions

    can be called by multiple threads simultaneously, and which pieces o data are shared among threads. Because

    Coverity Prevent understands a programs thread execution model, it is able to eliminate alse positives results

    due to idioms such as data hando between threads.

    A inal, important technique in discovering race conditions is the use o datalow analysis to track values both

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    11/130 Ensuring Code Quality in Multi-threaded Applications

    intra- and inter-procedurally. Tracking data low makes the analysis less brittle to simple reactorings such as

    common subexpression elimination that may preserve the semantics o a given program, but could still conuse

    the statistical inerence. Datalow analysis provides a more semantic representation o the program, making code

    analysis less sensitive to syntactic changes.

    Once guarded-by relationships have been inerred, a best practice that can be leveraged by Java developers

    is to permanently enorce the discovered relationships using annotations. Annotations are a Java language-

    supported mechanism or associating metadata with program elements. In this case, Coverity Prevent provides

    an annotation @GuardedBy(String lockname) that can be placed on ields to denote the lock that should be held

    when accessing the annotated ield. Note the change in Figure below rom our previous example in Figure 5.

    Figure 6: Updated Race Condition with Synchronized Increment Method

    In the example in Figure , Coverity Prevent SQS will report a deect in increment since the access to count is

    not protected by the appropriate lock.

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    12/13 Ensuring Code Quality in Multi-threaded Applications

    Multi-core hardware is clearly increasing sotware complexity by driving the need or multi-threaded

    applications. Based on the rising rate o multi-core hardware adoption in both enterprise and consumer devices,

    the challenge o creating multi-threaded applications is here to stay or sotware developers. In the coming years,

    multi-threaded application development will most likely become the dominant paradigm in sotware.

    As this shit continues, many development organizations will transition to multi-threaded application

    development on the ly. This creates new liabilities or development teams in terms o application quality and

    security, because their code is now vulnerable to a host o concurrency deectsa class o deect that can easily

    slip past manual code review and traditional testing techniques.

    Developers moving to multi-threaded applications need advanced new testing capabilities to help them control

    this new cause o sotware complexity. Because they are nearly impossible to replicate in dynamic testing

    environments, static analysis is uniquely suited to play an important role in eliminating concurrency deects early

    in the sotware development liecycle. However, due to their underlying complexity, some concurrency deect

    types (such as race conditions) have historically escaped detection by conventional static analysis tools.

    Today, sophisticated new technology rom Coverity is advancing the science o static analysis to help developers

    meet the challenge o creating multi-threaded applications. By arming developers with static analysis capabilities

    that detect complex concurrency deects early in the development liecycle, organizations will accelerate their

    ability to produce and release multi-threaded applications with greater conidence.

    Conclusion

  • 8/2/2019 Coverity Multi-threaded Whitepaper

    13/13

    About CoverityCoverity (www.coverity.com), the leader in improving sotware quality and security, is a privately held company

    headquartered in San Francisco. Coveritys groundbreaking technology enables developers to control complexity

    in the development process by automatically inding and helping to repair critical sotware deects and security

    vulnerabilities throughout the application liecycle. More than 450 leading companies including ARM, Phillips,

    RIM, Rockwell-Collins, Samsung and UBS rely on Coverity to help them ensure the delivery o superior sotware.

    Free TrialRequest a ree Coverity trial and see irst hand how to rapidly detect and remediate serious deects and

    vulnerabilities. No changes to your code are necessary. There are no limitations on code size, and you will

    receive a complimentary report detailing actionable analysis results. Register or the on-site evaluation at www.

    coverity.com or call us at (800) 83-893.

    Headquarters

    85 Berry Street, Suite 400

    San Francisco, CA 940

    (800) 83-893

    http://www.coverity.com

    [email protected]

    Boston

    30 Congress Street

    Suite 303

    Boston, MA 00

    () 933-500

    UK

    Coverity Limited

    Magdalen Centre

    Robert Robinson Avenue

    The Oxord Science Park

    Oxord OX4 4GA

    England

    Japan

    Coverity Asia Pacifc

    Level 3, Shinjuku Nomura Bldg.

    -- Nishi-Shinjuku, Shinjuku-ku

    Tokyo 3-053

    Japan