static void
list_assembly(const SqlDatabase::TransactionPtr &tx, int func_id)
{
    Events events;
    gather_events(tx, func_id);
    load_events(tx, func_id, events);

    SqlDatabase::StatementPtr stmt = tx->statement("select address, assembly from semantic_instructions where func_id = ?"
                                                   " order by position")->bind(0, func_id);
    for (SqlDatabase::Statement::iterator insn=stmt->begin(); insn!=stmt->end(); ++insn) {
        rose_addr_t addr = insn.get<rose_addr_t>(0);
        std::string assembly = insn.get<std::string>(1);
        Events::const_iterator ei=events.find(addr);

        // Assembly line prefix
        if (ei!=events.end() && ei->second.nexecuted>0) {
            std::cout <<std::setw(9) <<std::right <<ei->second.nexecuted <<"x ";
        } else {
            std::cout <<std::string(11, ' ');
        }

        // Assembly instruction
        std::cout <<"| " <<StringUtility::addrToString(addr) <<":  " <<assembly <<"\n";

        if (ei!=events.end())
            show_events(ei->second);
    }
}
static void
list_combined(const SqlDatabase::TransactionPtr &tx, int func_id, bool show_assembly)
{
    CloneDetection::FilesTable files(tx);

    Events events;
    gather_events(tx, func_id);
    load_events(tx, func_id, events/*out*/);
    gather_instructions(tx, func_id, events);

    Listing listing;
    gather_source_code(tx);
    load_source_code(tx, listing/*out*/);

    // Get lines of assembly code and insert them into the correct place in the Listing.
    if (show_assembly) {
        SqlDatabase::StatementPtr stmt = tx->statement("select"
                                                       // 0                1              2              3
                                                       " insn.src_file_id, insn.src_line, insn.position, insn.address,"
                                                       // 4             5        6
                                                       " insn.assembly, func.id, func.name"
                                                       " from tmp_insns as insn"
                                                       " join semantic_functions as func on insn.func_id = func.id"
                                                       " order by position");
        for (SqlDatabase::Statement::iterator row=stmt->begin(); row!=stmt->end(); ++row) {
            int src_file_id = row.get<int>(0);
            int src_line_num = row.get<int>(1);
            SourcePosition srcpos(src_file_id, src_line_num);
            int pos = row.get<int>(2);
            rose_addr_t addr = row.get<rose_addr_t>(3);
            std::string assembly = row.get<std::string>(4);
            int func_id = row.get<int>(5);
            std::string func_name = row.get<std::string>(6);
            listing[srcpos].assembly_code.insert(std::make_pair(addr, AssemblyCode(pos, addr, assembly, func_id, func_name)));
        }

        // Listing header
        std::cout <<"WARNING: This listing should be read cautiously. It is ordered according to the\n"
                  <<"         source code with assembly lines following the source code line from which\n"
                  <<"         they came.  However, the compiler does not always generate machine\n"
                  <<"         instructions in the same order as source code.  When a discontinuity\n"
                  <<"         occurs in the assembly instruction listing, it will be marked by a \"#\"\n"
                  <<"         character.  The assembly instructions are also numbered according to\n"
                  <<"         their relative positions in the binary function.\n"
                  <<"\n"
                  <<"         The prefix area contains either source location information or test trace\n"
                  <<"         information.  Note that trace information might be incomplete because\n"
                  <<"         tracing was disabled or only partially enabled, or the trace includes\n"
                  <<"         instructions that are not present in this function listing (e.g., when\n"
                  <<"         execution follows a CALL instruction). The following notes are possible:\n"
                  <<"           * \"Nx\" where N is an integer indicates that this instruction\n"
                  <<"             was reached N times during testing.  These notes are typically\n"
                  <<"             only attached to the first instruction of a basic block and only\n"
                  <<"             if the trace contains EV_REACHED events.  Lack of an Nx notation\n"
                  <<"             doesn't necessarily mean that the basic block was not reached, it\n"
                  <<"             only means that there is no EV_REACHED event for that block.\n"
                  <<"           * \"N<\" where N is an integer indicates that the instruction\n"
                  <<"             on the previous line consumed N inputs. Information about the\n"
                  <<"             inputs is listed on the right side of this line.\n"
                  <<"           * \"N>\" where N is an integer indicates that the instruction\n"
                  <<"             on the previous line produced N memory outputs. Information about the\n"
                  <<"             outputs is listed on the right side of this line. Only the final\n"
                  <<"             write to a memory address is considered a true output, and such\n"
                  <<"             writes will be marked with the string \"final\".\n"
                  <<"           * \"BR\" indicates that the instruction on the previous line is a\n"
                  <<"             control flow branch point. The right side of the line shows more\n"
                  <<"             detailed information about how many times the branch was taken.\n"
                  <<"           * \"FAULT\" indicates that the test was terminated at the previous\n"
                  <<"             instruction. The right side of the line shows the distribution of\n"
                  <<"             faults that occurred here.\n"
                  <<"\n"
                  <<"                /------------- Prefix area\n"
                  <<" /-------------/-------------- Source file ID or assembly function ID\n"
                  <<" |     /------/--------------- Source line number or assembly instruction index\n"
                  <<" |     |   /-/---------------- Instruction out-of-order indicator\n"
                  <<" |     |   |/     /----------- Instruction virtual address\n"
                  <<" |     |   |      |\n"
                  <<"vvvv vvvvv/|      |\n"
                  <<"vvvvvvvvvv v vvvvvvvvvv\n";
    }

    // Show the listing
    int prev_func_id = -1, prev_position = -1;
    std::set<int> seen_files;
    for (Listing::iterator li=listing.begin(); li!=listing.end(); ++li) {
        int file_id = li->first.file_id;
        if (seen_files.insert(file_id).second) {
            if (file_id>=0) {
                std::cout <<"\n" <<std::setw(4) <<std::right <<file_id <<".file  |"
                          <<(opt.colorize?"\033[33;4m":"") <<files.name(file_id) <<(opt.colorize?"\033[m":"") <<"\n";
            } else {
                std::cout <<"\n" <<std::string(11, ' ') <<"|"
                          <<(opt.colorize?"\033[33;4m":"") <<"instructions not associated with a source file"
                          <<(opt.colorize?"\033[m":"") <<"\n";
            }
        }
        if (file_id>=0) {
            std::cout <<std::setw(4) <<std::right <<file_id <<"." <<std::setw(6) <<std::left <<li->first.line_num
                      <<"|"
                      <<(opt.colorize?"\033[34m":"")
                      <<StringUtility::untab(li->second.source_code)
                      <<(opt.colorize?"\033[m":"") <<"\n";
        }

        for (Instructions::iterator ii=li->second.assembly_code.begin(); ii!=li->second.assembly_code.end(); ++ii) {
            const AssemblyCode assm = ii->second;
            if (assm.func_id!=prev_func_id) {
                std::cout <<std::string(11, ' ') <<"# "
                          <<(opt.colorize?"\033[33;4m":"") <<"function " <<StringUtility::numberToString(assm.func_id);
                if (!assm.func_name.empty())
                    std::cout <<" <" <<assm.func_name <<">";
                std::cout <<(opt.colorize?"\033[m":"") <<"\n";
            }

            Events::const_iterator ei=events.find(assm.addr);
            std::cout <<std::setw(4) <<std::right <<assm.func_id <<"." <<std::setw(6) <<std::left <<assm.pos
                      <<(prev_func_id==assm.func_id && prev_position+1==assm.pos ? "|" : "#");

            if (ei!=events.end() && ei->second.nexecuted>0) {
                std::cout <<std::setw(9) <<std::right <<ei->second.nexecuted <<"x ";
            } else {
                std::cout <<std::string(11, ' ');
            }

            std::cout <<StringUtility::addrToString(assm.addr) <<":  "
                      <<(opt.colorize?"\033[32m":"") <<assm.assembly <<(opt.colorize?"\033[m":"") <<"\n";

            if (ei!=events.end())
                show_events(ei->second);

            prev_func_id = assm.func_id;
            prev_position = assm.pos;
        }
    }
}