/*******************************************************************************
 * Copyright (c) 2007, 2013 Nokia and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Nokia - initial API and implementation
 *     Ling Wang (Nokia) bug 201000
 *     Serge Beauchamp (Freescale Semiconductor) - Bug 421070
 *     Red Hat Inc. - add debuginfo and macro section support
 *******************************************************************************/

package org.eclipse.cdt.utils.debug.dwarf;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.DatatypeConverter;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.ICompileOptionsFinder;
import org.eclipse.cdt.core.ISymbolReader;
import org.eclipse.cdt.utils.coff.Coff.SectionHeader;
import org.eclipse.cdt.utils.coff.PE;
import org.eclipse.cdt.utils.debug.IDebugEntryRequestor;
import org.eclipse.cdt.utils.elf.Elf;
import org.eclipse.cdt.utils.elf.Elf.Section;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;

/**
 * Light-weight parser of Dwarf2 data which is intended for getting only 
 * source files that contribute to the given executable.
 */
public class DwarfReader extends Dwarf implements ISymbolReader, ICompileOptionsFinder {

	// These are sections that need be parsed to get the source file list.
	final static String[] DWARF_SectionsToParse = { 
		DWARF_DEBUG_INFO, 
		DWARF_DEBUG_LINE,
		DWARF_DEBUG_ABBREV, 
		DWARF_DEBUG_STR, // this is optional. Some compilers don't generate it.
		DWARF_DEBUG_MACRO, };
	
	final static String[] DWARF_ALT_SectionsToParse = { 
		DWARF_DEBUG_STR, 
		DWARF_DEBUG_MACRO };

	private final Collection<String>	m_fileCollection = new HashSet<String>();
	private final Map<Long, String>  m_stmtFileMap = new HashMap<Long, String>();
	private final Map<String, ArrayList<String>> m_compileOptionsMap = new HashMap<String, ArrayList<String>>();
	private String[] 	m_fileNames = null;
	private boolean		m_parsed = false;
	private boolean		m_macros_parsed = false;
	private final ArrayList<Integer>	m_parsedLineTableOffsets = new ArrayList<Integer>();
	private long			m_parsedLineTableSize = 0;
		
	public DwarfReader(String file) throws IOException {
		super(file);
	}

	public DwarfReader(Elf exe) throws IOException {
		super(exe);
	}

	/**
	 * @since 5.1
	 */
	public DwarfReader(PE exe) throws IOException  {
		super(exe);
	}

	// Override parent.
	// 
	@Override
	public void init(Elf exe) throws IOException {
		Elf.ELFhdr header = exe.getELFhdr();
		isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB;

		IPath debugInfoPath = new Path(exe.getFilename());
		Elf.Section[] sections = exe.getSections();
		
		boolean have_build_id = false;
		
		// Look for a special GNU build-id note which means the debug data resides in a separate
		// file with a name based on the build-id.
		for (Section section : sections) {
			if (section.sh_type == Elf.Section.SHT_NOTE) {
				ByteBuffer data = section.mapSectionData();
				if (data.remaining() > 12) {
					try {
						// Read .note section, looking to see if it is named "GNU" and is of GNU_BUILD_ID type
						@SuppressWarnings("unused")
						int name_sz = read_4_bytes(data);
						int data_sz = read_4_bytes(data);
						int note_type = read_4_bytes(data);

						String noteName = readString(data);
						String buildId = null;
						if (noteName.equals("GNU") && note_type == Elf.Section.NT_GNU_BUILD_ID) { //$NON-NLS-1$
							// We have the special GNU build-id note section.  Skip over the name to
							// a 4-byte boundary.
							byte[] byteArray = new byte[data_sz];
							while ((data.position() & 0x3) != 0)
								data.get();
							int i = 0;
							// Read in the hex bytes from the note section's data.
							while (data.hasRemaining() && data_sz-- > 0) {
								byteArray[i++] = data.get();
							}
							// The build-id location is taken by converting the binary bytes to hex string.
							// The first byte is used as a directory specifier (e.g. 51/a4578fe2).
							String bName = DatatypeConverter.printHexBinary(byteArray).toLowerCase();
							buildId = bName.substring(0, 2) + "/" + bName.substring(2) + ".debug"; //$NON-NLS-1$ //$NON-NLS-2$
							// The build-id file should be in the special directory /usr/lib/debug/.build-id
							IPath buildIdPath = new Path("/usr/lib/debug/.build-id").append(buildId); //$NON-NLS-1$
							File buildIdFile = buildIdPath.toFile();
							if (buildIdFile.exists()) {
								// if the debug file exists from above, open it and get the section info from it
								Elf debugInfo = new Elf(buildIdFile.getCanonicalPath());
								sections = debugInfo.getSections();
								have_build_id = true;
								debugInfoPath = new Path(buildIdFile.getCanonicalPath()).removeLastSegments(1);
								break;
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
						CCorePlugin.log(e);
					}
				}
			}
		}
		
		if (!have_build_id) {
			// No build-id.  Look for a .gnu_debuglink section which will have the name of the debug info file
			Elf.Section gnuDebugLink = exe.getSectionByName(DWARF_GNU_DEBUGLINK);
			if (gnuDebugLink != null) {
				ByteBuffer data = gnuDebugLink.mapSectionData();
				if (data != null) { // we have non-empty debug info link
					try {
						// name is zero-byte terminated character string
						String debugName = ""; //$NON-NLS-1$
						if (data.hasRemaining()) {
							int c;
							StringBuffer sb = new StringBuffer();
							while ((c = data.get()) != -1) {
								if (c == 0) {
									break;
								}
								sb.append((char) c);
							}
							debugName = sb.toString();
						}
						if (debugName.length() > 0) {
							// try and open the debug info from 3 separate places in order
							File debugFile = null;
							IPath exePath = new Path(exe.getFilename());
							IPath p = exePath.removeLastSegments(1);
							// 1. try and open the file in the same directory as the executable
							debugFile = p.append(debugName).toFile();
							if (!debugFile.exists()) {
								// 2. try and open the file in the .debug directory where the executable is 
								debugFile = p.append(".debug").append(debugName).toFile(); //$NON-NLS-1$
								if (!debugFile.exists())
									// 3. try and open /usr/lib/debug/$(EXE_DIR)/$(DEBUGINFO_NAME)
									debugFile = new Path("/usr/lib/debug").append(p).append(debugName).toFile(); //$NON-NLS-1$
							}
							if (debugFile.exists()) {
								// if the debug file exists from above, open it and get the section info from it
								Elf debugInfo = new Elf(debugFile.getCanonicalPath());
								sections = debugInfo.getSections();
								debugInfoPath = new Path(debugFile.getCanonicalPath()).removeLastSegments(1);
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
						CCorePlugin.log(e);
					}
				}

			}
		}
		
		// Read in sections (and only the sections) we care about.
		//
		for (Section section : sections) {
			String name = section.toString();
			if (name.equals(DWARF_GNU_DEBUGALTLINK)) {
				ByteBuffer data = section.mapSectionData();
				try {
					// name is zero-byte terminated character string
					String altInfoName = readString(data);
					if (altInfoName.length() > 0) {
						IPath altPath = new Path(altInfoName);
						if (!altPath.isAbsolute()) {
							altPath = debugInfoPath.append(altPath);
						}
						File altFile = altPath.toFile();
						if (altFile.exists()) {
							Elf altInfo = new Elf(altFile.getCanonicalPath());
							Elf.Section[] altSections = altInfo.getSections();
							for (Section altSection : altSections) {
								String altName = altSection.toString();
								for (String element : DWARF_ALT_SectionsToParse) {
									if (altName.equals(element)) {
										try {
											dwarfAltSections.put(element, altSection.mapSectionData());
										} catch (Exception e) {
											e.printStackTrace();
											CCorePlugin.log(e);
										}
									}
								}
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
					CCorePlugin.log(e);
				}
			} 
			else {
				for (String element : DWARF_SectionsToParse) {
					if (name.equals(element)) {
						// catch out of memory exceptions which might happen trying to
						// load large sections (like .debug_info).  not a fix for that
						// problem itself, but will at least continue to load the other
						// sections.
						try {
							dwarfSections.put(element, section.mapSectionData());
						} catch (Exception e) {
							CCorePlugin.log(e);
						}
					}
				}
			}
		}
		
		// Don't print during parsing.
		printEnabled = false;
		m_parsed = false;
	}

	@Override
	public void init(PE exe) throws IOException {

		isLE = true;
		SectionHeader[] sections = exe.getSectionHeaders();

		for (int i = 0; i < sections.length; i++) {
			String name = new String(sections[i].s_name).trim();
			if (name.startsWith("/")) //$NON-NLS-1$
			{
				int stringTableOffset = Integer.parseInt(name.substring(1));
				name = exe.getStringTableEntry(stringTableOffset);
			}
			for (String element : Dwarf.DWARF_SCNNAMES) {
				if (name.equals(element)) {
					try {
						dwarfSections.put(element, sections[i].mapSectionData());
					} catch (Exception e) {
						CCorePlugin.log(e);
					}
				}
			}
		}
		// Don't print during parsing.
		printEnabled = false;
		m_parsed = false;
	}

	/*
	 * Parse line table data of a compilation unit to get names of all source files
	 * that contribute to the compilation unit. 
	 */
	void parseSourceInCULineInfo(
			String cuCompDir,	// compilation directory of the CU 
			int cuStmtList) 	// offset of the CU line table in .debug_line section 
	{
		ByteBuffer data = dwarfSections.get(DWARF_DEBUG_LINE);
		if (data != null) {
			try {
				data.position(cuStmtList);
				
				/* Read line table header:
				 * 
				 *  total_length:				4/12 bytes (excluding itself)
				 *  version:					2
				 *  prologue length:			4/8 bytes (depending on section version)
				 *  minimum_instruction_len:	1
				 *  maximum_operations_per_instruction 1 - it is defined for version >= 4 
				 *  default_is_stmt:			1
				 *  line_base:					1
				 *  line_range:					1
				 *  opcode_base:				1
				 *  standard_opcode_lengths:	(value of opcode_base)
				 */
				
				// Remember the CU line tables we've parsed.
				Integer cuOffset = new Integer(cuStmtList);
				
				boolean dwarf64Bit = false;
				if (! m_parsedLineTableOffsets.contains(cuOffset)) {
					m_parsedLineTableOffsets.add(cuOffset);

					// Note the length does not including the "length" field(s) itself.
					InitialLengthValue length = readInitialLengthField(data);
					dwarf64Bit = length.offsetSize == 8;
					m_parsedLineTableSize += length.length + (dwarf64Bit ? 12 : 4);
				}
				else {
					// Compiler like ARM RVCT may produce several CUs for the
					// same source files.
					return;
				}
				
				short version = read_2_bytes(data);	
				// Skip the following till "opcode_base"
				short skip_bytes = 8;
				if (version >= 4)
					skip_bytes += 1; // see maximum_operations_per_instruction
				if (dwarf64Bit)
					skip_bytes += 4; // see prologue length for 64-bit DWARF format
				data.position(data.position() + skip_bytes);
				int opcode_base = data.get();
				data.position(data.position() + opcode_base - 1);

				// Read in directories.
				//
				ArrayList<String>	dirList = new ArrayList<String>();

				// Put the compilation directory of the CU as the first dir
				dirList.add(cuCompDir);
				
				String 			str, fileName;
				
				while (true) {
					str = readString(data);
					if (str.length() == 0)
						break;
					// If the directory is relative, append it to the CU dir
					IPath dir = new Path(str);
					if(!dir.isAbsolute())
						dir = new Path(cuCompDir).append(str);
					dirList.add(dir.toString());
				}
				
				// Read file names
				//
				long	leb128;
				while (true) {
					fileName = readString(data);
					if (fileName.length() == 0)	// no more file entry
						break;
					
					// dir index
					leb128 = read_unsigned_leb128(data);
					
					addSourceFile(dirList.get((int)leb128), fileName);
					
					// Skip the followings
					//
					// modification time
					leb128 = read_unsigned_leb128(data);

					// file size in bytes
					leb128 = read_unsigned_leb128(data);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/*
	 * Check if there are any line tables in .debug_line section that are
	 * not referenced by any TAG_compile_units. If yes, add source files
	 * in those table entries to our "m_fileCollection".
	 * If the compiler/linker is fully dwarf standard compliant, that should 
	 * not happen. But that case does exist, hence this workaround. 
	 * .................. LWang. 08/24/07
	 */
	private void getSourceFilesFromDebugLineSection()
	{
		ByteBuffer data = dwarfSections.get(DWARF_DEBUG_LINE);
		if (data == null) 
			return;
		
		int sectionSize = data.capacity();
		int minHeaderSize = 16;

		// Check if there is data in .debug_line section that is not parsed
		// yet by parseSourceInCULineInfo().
		if (m_parsedLineTableSize >= sectionSize - minHeaderSize)
			return;
		
		// The .debug_line section contains a list of line tables
		// for compile_units. We'll iterate through all line tables
		// in the section.
		/*
		 * Line table header for one compile_unit:
		 * 
		 * total_length: 			4/12 bytes (excluding itself)
		 * version:					2
		 * prologue length:			4/8 bytes (depending on section version)
		 * minimum_instruction_len: 1 
		 * maximum_operations_per_instruction 1 - it is defined for version >= 4 
		 * default_is_stmt:			1
		 * line_base:				1
		 * line_range:				1
		 * opcode_base:				1
		 * standard_opcode_lengths: (value of opcode_base)		 */

		int lineTableStart = 0;	// offset in the .debug_line section
		
		try {
			while (lineTableStart < sectionSize - minHeaderSize) {
				data.position(lineTableStart);

				Integer currLineTableStart = new Integer(lineTableStart);
				
				// Read length of the line table for one compile unit
				// Note the length does not including the "length" field(s) itself.
				InitialLengthValue sectionLength = readInitialLengthField(data);


				// Record start of next CU line table
				boolean dwarf64Bit = sectionLength.offsetSize == 8;
				lineTableStart += (int)(sectionLength.length + (dwarf64Bit ? 12 : 4));
				
				m_parsedLineTableSize += sectionLength.length + (dwarf64Bit ? 12 : 4);

				// According to Dwarf standard, the "tableLength" should cover the
				// the whole CU line table. But some compilers (e.g. ARM RVCT 2.2)
				// produce extra padding (1 to 3 bytes) beyond that in order for 
				// "lineTableStart" to be aligned at multiple of 4. The padding
				// bytes are beyond the "tableLength" and not indicated by 
				// any flag, which I believe is not Dwarf2 standard compliant.
				// How to determine if that type of padding exists ? 
				// I don't have a 100% safe way. But following hacking seems
				// good enough in practice.........08/26/07
				if (lineTableStart < sectionSize - minHeaderSize && 
						(lineTableStart & 0x3) != 0) 
				{
					int savedPosition = data.position();
					data.position(lineTableStart);
					
					long ltLength = dwarf64Bit ? read_8_bytes(data) : read_4_bytes(data);
					
					int dwarfVer = read_2_bytes(data);
					int minInstLengh = data.get(data.position() + (dwarf64Bit ? 8 : 4));
					
					boolean dataValid = 
						ltLength > minHeaderSize && 
						ltLength < 16*64*1024 &&   // One source file has that much line data ? 
						dwarfVer > 0 &&	dwarfVer < 5 &&  // ver 5 is still draft at present.
						minInstLengh > 0 && minInstLengh <= 8;
						
					if (! dataValid)	// padding exists !
						lineTableStart = (lineTableStart+3) & ~0x3;
					
					data.position(savedPosition);
				}
				
				if (m_parsedLineTableOffsets.contains(currLineTableStart))
					// current line table has already been parsed, skip it.
					continue;

				short version = read_2_bytes(data);
				
				// Skip following fields till "opcode_base"
				short skip_bytes = 8;
				if (version >= 4)
					skip_bytes += 1; // see maximum_operations_per_instruction
				if (dwarf64Bit)
					skip_bytes += 4; // see prologue length for 64-bit DWARF format
				data.position(data.position() + skip_bytes);
				int opcode_base = data.get();
				data.position(data.position() + opcode_base - 1);

				// Read in directories.
				//
				ArrayList<String> dirList = new ArrayList<String>();

				String str, fileName;

				// first dir should be TAG_comp_dir from CU, which we don't have here.
				dirList.add(""); //$NON-NLS-1$
				
				while (true) {
					str = readString(data);
					if (str.length() == 0)
						break;
					dirList.add(str);
				}

				// Read file names
				//
				long leb128;
				while (true) {
					fileName = readString(data);
					if (fileName.length() == 0) // no more file entry
						break;

					// dir index. Note "0" is reserved for compilation directory. 
					leb128 = read_unsigned_leb128(data);

					addSourceFile(dirList.get((int) leb128), fileName);

					// Skip the followings
					//
					// modification time
					leb128 = read_unsigned_leb128(data);

					// file size in bytes
					leb128 = read_unsigned_leb128(data);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
	}

	@Override
	public String[] getSourceFiles() {
		if (!m_parsed) {
			m_fileCollection.clear();

			getSourceFilesFromDebugInfoSection();
			
			getSourceFilesFromDebugLineSection();
			
			m_parsed = true;

			m_fileNames = new String[m_fileCollection.size()];
			m_fileCollection.toArray(m_fileNames);
		}

		return m_fileNames;
	}

	/*
	 * Get source file names from compile units (CU) in .debug_info section,
	 * which will also search line table for the CU in .debug_line section.
	 * 
	 * The file names are stored in member "m_fileCollection".
	 */
	private void getSourceFilesFromDebugInfoSection() {
		// This will parse the data in .debug_info section which
		// will call this->processCompileUnit() to get source files.
		parse(null);
	}

	private String addSourceFileWithStmt(String dir, String name, int stmt) {
		String fullName = addSourceFile(dir, name);
		m_stmtFileMap.put(Long.valueOf(stmt), fullName);
		return fullName;
	}

	private String addSourceFile(String dir, String name)
	{
		if (name == null || name.length() == 0)
			return null;
		
		if (name.charAt(0) == '<')	//  don't count the entry "<internal>" from GCCE compiler
			return null;
		
		String fullName = name;
		
		IPath dirPa = new Path(dir);
		IPath pa = new Path(name);
		
		// Combine dir & name if needed.
		if (!pa.isAbsolute() && dir.length() > 0)
			pa = dirPa.append(pa);

		// This convert the path to canonical path (but not necessarily absolute, which
		// is different from java.io.File.getCanonicalPath()).
		fullName = pa.toOSString();
		
		if (!m_fileCollection.contains(fullName))
			m_fileCollection.add(fullName);
		
		return fullName;
	}
	
	// Override parent: only handle TAG_Compile_Unit.
	@Override
	void processDebugInfoEntry(IDebugEntryRequestor requestor, AbbreviationEntry entry, List<Dwarf.AttributeValue> list) {
		int tag = (int) entry.tag;
		switch (tag) {
			case DwarfConstants.DW_TAG_compile_unit :
				processCompileUnit(requestor, list);
				break;
			default:
				break;
		}
	}

	// Override parent.
	// Just get the file name of the CU.
	// Argument "requestor" is ignored.
	@Override
	void processCompileUnit(IDebugEntryRequestor requestor, List<AttributeValue> list) {
		
		String cuName, cuCompDir;
		int		stmtList = -1;
		
		cuName = cuCompDir = ""; //$NON-NLS-1$
		
		for (int i = 0; i < list.size(); i++) {
			AttributeValue av = list.get(i);
			try {
				int name = (int)av.attribute.name;
				switch(name) {
					case DwarfConstants.DW_AT_name:
						cuName = (String)av.value;
						break;
					case DwarfConstants.DW_AT_comp_dir:
						cuCompDir = (String)av.value;
						break;
					case DwarfConstants.DW_AT_stmt_list:
						stmtList = ((Number)av.value).intValue();
						break;
					default:
						break;
				}
			} catch (ClassCastException e) {
			}
		}

		addSourceFileWithStmt(cuCompDir, cuName, stmtList);
		if (stmtList > -1)	// this CU has "stmt_list" attribute
			parseSourceInCULineInfo(cuCompDir, stmtList);
	}
	
	/**
	 * @since 5.2
	 */
	@Override
	public String[] getSourceFiles(IProgressMonitor monitor) {
		return getSourceFiles();
	}

	private class OpcodeInfo {
		private int numArgs;
		private final boolean offset_size_8;
		ArrayList<Integer> argTypes;
		
		public OpcodeInfo (boolean offset_size_8) {
			this.offset_size_8 = offset_size_8;
		}
		
		public void setNumArgs (int numArgs) {
			this.numArgs = numArgs;
		}
		
		public void addArgType(int argType) {
			argTypes.add(Integer.valueOf(argType));
		}
		
		public void readPastEntry(ByteBuffer data) {
			for (int i = 0; i < numArgs; ++i) {
				int argType = argTypes.get(i).intValue();
				switch (argType) {
				case DwarfConstants.DW_FORM_flag:
				case DwarfConstants.DW_FORM_data1:
					data.get();
					break;
				case DwarfConstants.DW_FORM_data2:
					data.getShort();
					break;
				case DwarfConstants.DW_FORM_data4:
					data.getInt();
					break;
				case DwarfConstants.DW_FORM_data8:
					data.getLong();
					break;
				case DwarfConstants.DW_FORM_sdata:
				case DwarfConstants.DW_FORM_udata:
					try {
						read_signed_leb128(data);
					} catch (IOException e) {
						e.printStackTrace();
					}
					break;
				case DwarfConstants.DW_FORM_block: {
					try {
						long off = read_signed_leb128(data);
						data.position((int)(data.position() + off));
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				break;
				case DwarfConstants.DW_FORM_block1: {
					int off = data.get();
					data.position(data.position() + off + 1);
				}
				break;
				case DwarfConstants.DW_FORM_block2: {
					int off = data.getShort();
					data.position(data.position() + off + 2);
				}
				break;
				case DwarfConstants.DW_FORM_block4: {
					int off = data.getInt();
					data.position(data.position() + off + 4);
				}
				break;
				case DwarfConstants.DW_FORM_string:
					while (data.get() != 0) {
						// loop until we find 0 byte
					}
					break;
				case DwarfConstants.DW_FORM_strp:
				case DwarfConstants.DW_FORM_GNU_strp_alt:
				case DwarfConstants.DW_FORM_GNU_ref_alt:
				case DwarfConstants.DW_FORM_sec_offset:
					if (offset_size_8)
						data.getLong();
					else
						data.getInt();
					break;
				}
			}
		}
	}
	
	// Convert a macro to its command line form.
	private String getCommandLineMacro(String macro) {
		String commandLineMacro = "-D" + macro; //$NON-NLS-1$
		commandLineMacro = commandLineMacro.replaceFirst(" ", "="); //$NON-NLS-1$ //$NON-NLS-2$
		return commandLineMacro;
	}

	// Go through regular and alt macro sections and find any macros that were set on the
	// compilation command line (e.g. gcc -Dflagx=1 my.c).  Built-in macros and macros that
	// are set within files (source and include) are ignored since they can and will be
	// discovered by indexing.
	private void getCommandMacrosFromMacroSection() {
		ByteBuffer data = dwarfSections.get(DWARF_DEBUG_MACRO);
		ByteBuffer str = dwarfSections.get(DWARF_DEBUG_STR);
		ByteBuffer altdata = dwarfAltSections.get(DWARF_DEBUG_MACRO);
		ByteBuffer altstr = dwarfAltSections.get(DWARF_DEBUG_STR);
		Set<String> fixupList = new HashSet<String>();
		Set<String> fixupAltList = new HashSet<String>();
		boolean DEBUG = false;
		if (data == null)
			return;

		HashMap <Long, ArrayList<String>> t_macros = new HashMap<Long, ArrayList<String>>();
		HashMap <Long, ArrayList<String>> t_alt_macros = new HashMap<Long, ArrayList<String>>();
		
		// Parse the macro section, looking for command-line macros meant for compiling files (i.e.
		// not internal macro definitions in headers or C/C++ files.  Keep track of any forward
		// references to fix-up later when we have a complete list of command-line macros.
		parseMacroSection(data, str, altstr, fixupList, fixupAltList, "=FIXUP=", DEBUG, t_macros); //$NON-NLS-1$
       
		// Check if there is an alternate macro section.  If there is, parse the alternate section, but any references
		// found there should be considered referring to the alternate string and macro sections.
		// All forward reference fix-ups should be put on the alternate fix-up list.
		if (altdata != null) {
			if (DEBUG)
				System.out.println("Processing Alternate Macro Section"); //$NON-NLS-1$
			parseMacroSection(altdata, altstr, altstr, fixupAltList, fixupAltList, "=FIXUPALT=", DEBUG, t_alt_macros); //$NON-NLS-1$
		}
			
		// Fix up all forward references from transparent includes
		fixupMacros(fixupList, "=FIXUP=", DEBUG, t_macros); //$NON-NLS-1$
		
		// Fix up all forward references from transparent alt includes
		if (DEBUG)
			System.out.println("Fix up forward references in alternate macro section"); //$NON-NLS-1$
		fixupMacros(fixupAltList, "=FIXUPALT=", DEBUG, t_alt_macros); //$NON-NLS-1$
	}

	// Fix up forward references made by transparent includes now that a complete macro list has been retrieved.
	private void fixupMacros(Set<String> fixupList, String fixupMarker, boolean DEBUG,
			HashMap<Long, ArrayList<String>> t_macros) {
		for (String name: fixupList) {
			ArrayList<String> macros = m_compileOptionsMap.get(name);
			for (int i = 0; i < macros.size(); ++i) {
				String macroLine = macros.get(i);
				if (macroLine.startsWith(fixupMarker)) {
					Long offset = Long.valueOf(macroLine.substring(7));
					if (DEBUG)
						System.out.println("Found fixup needed for offset: " + offset + " for file: " + name); //$NON-NLS-1$ //$NON-NLS-2$
					ArrayList<String> insertMacros = t_macros.get(offset);
					if (DEBUG)
						System.out.println("insert macros are: " + insertMacros.toString()); //$NON-NLS-1$
					macros.remove(i);
					macros.addAll(i, insertMacros);
					i += insertMacros.size();
				}
			}
			m_compileOptionsMap.put(name,  macros); // replace updated list
		}
	}

	// Parse a macro section, looking for command-line macros that are used as flags to compile source files.
	// Keep track of any forward references to macros not yet defined in the file.  We will later
	// fix-up these references when we have seen all command-line macros for the file.
	private void parseMacroSection(ByteBuffer data, ByteBuffer str, ByteBuffer altstr,
			Set<String> fixupList, Set<String> fixupAltList, String fixupMarker, boolean DEBUG,
			HashMap<Long, ArrayList<String>> t_macros) {
		byte op;
		while (data.hasRemaining()) {
			try {
				int original_position = data.position();
				int type = read_2_bytes(data);
				byte flags = data.get();
				boolean offset_size_8;
				long lt_offset = -1;
				String fileName = null;
				
				HashMap<Integer, OpcodeInfo> opcodeInfos = null;

				if (DEBUG)
					System.out.println("type is " + type); //$NON-NLS-1$

				// bottom bit 0 tells us whether we have 8 byte offsets or 4 byte offsets
				offset_size_8 = (flags & 0x1) == 1;
				if (DEBUG)
					System.out.println("offset size is " + (offset_size_8 ? 8 : 4)); //$NON-NLS-1$
				// bit 1 indicates we have an offset from the start of .debug_line section
				if ((flags & 0x2) != 0) {
					lt_offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data));
					fileName = m_stmtFileMap.get(Long.valueOf(lt_offset));
					if (DEBUG)
						System.out.println("debug line offset is " + lt_offset); //$NON-NLS-1$
				}

				// if bit 2 flag is on, then we have a macro entry table which may
				// have non-standard entry types defined which we need to know when
				// we come across macro entries later
				if ((flags & 0x4) != 0) {
					opcodeInfos = new HashMap<Integer, OpcodeInfo>();
					int num_opcodes = data.get();
					for (int i = 0; i < num_opcodes; ++i) {
						OpcodeInfo info = new OpcodeInfo(offset_size_8);

						int opcode = data.get();
						long numArgs = read_unsigned_leb128(data);
						info.setNumArgs((int)numArgs);
						for (int j = 0; j < numArgs; ++j) {
							int argType = data.get();
							info.addArgType(argType);
						}
						opcodeInfos.put(Integer.valueOf(opcode), info);
					}
				}

				ArrayList<String> macros = new ArrayList<String>();

				boolean done = false;

				while (!done) {
					op = data.get();
					switch (op) {

					case DwarfConstants.DW_MACRO_start_file: {
						long filenum;
						long lineno;
						lineno = read_signed_leb128(data);
						filenum = read_signed_leb128(data);
						// All command line macros are defined as being included before start of file
						if (filenum == 1 && lt_offset >= 0) {
							// we have a source file so add all macros defined before it with lineno 0
							m_compileOptionsMap.put(fileName, macros);
							if (DEBUG)
								System.out.println("following macros found for file " + macros.toString()); //$NON-NLS-1$
							macros = new ArrayList<String>();
						}
						if (fileName != null)
							if (DEBUG)
								System.out.println(" DW_MACRO_start_file - lineno: " + lineno + " filenum: " //$NON-NLS-1$ //$NON-NLS-2$
										+ filenum + " " + fileName); //$NON-NLS-1$
						else if (DEBUG)
							System.out.println(" DW_MACRO_start_file - lineno: " + lineno + " filenum: " //$NON-NLS-1$ //$NON-NLS-2$
									+ filenum);
					}
					break;
					case DwarfConstants.DW_MACRO_end_file: {
						if (DEBUG)
							System.out.println(" DW_MACRO_end_file"); //$NON-NLS-1$
					}
					break;
					case DwarfConstants.DW_MACRO_define: {
						long lineno;
						String string;
						lineno = read_signed_leb128(data);
						string = readString(data);
						if (lineno == 0)
							macros.add(getCommandLineMacro(string));
						if (DEBUG)
							System.out.println(" DW_MACRO_define - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$
									+ string);
					}
					break;
					case DwarfConstants.DW_MACRO_undef: {
						long lineno;
						String macro;
						lineno = read_signed_leb128(data);
						macro = readString(data);
						if (DEBUG)
							System.out.println(" DW_MACRO_undef - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$
									+ macro);
					}
					break;
					case DwarfConstants.DW_MACRO_define_indirect: {
						long lineno;
						long offset;
						lineno = read_signed_leb128(data);
						offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data));
						str.position((int)offset);
						String macro = readString(str);
						if (lineno == 0)
							macros.add(getCommandLineMacro(macro));
						if (DEBUG)
							System.out.println(" DW_MACRO_define_indirect - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$
									+ macro);
					}
					break;
					case DwarfConstants.DW_MACRO_define_indirect_alt: {
						long lineno;
						long offset;
						lineno = read_signed_leb128(data);
						offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data));
						altstr.position((int)offset);
						String macro = readString(altstr);
						if (lineno == 0)
							macros.add(getCommandLineMacro(macro));
						if (DEBUG)
							System.out.println(" DW_MACRO_define_indirect_alt - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$
									+ macro);
					}
					break;
					case DwarfConstants.DW_MACRO_undef_indirect: {
						long lineno;
						long offset;
						String macro;
						lineno = read_signed_leb128(data);
						offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data));
						str.position((int)offset);
						macro = readString(str);
						if (DEBUG)
							System.out.println(" DW_MACRO_undef_indirect - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$
									+ macro);
					}
					break;
					case DwarfConstants.DW_MACRO_undef_indirect_alt: {
						long lineno;
						long offset;
						String macro;
						lineno = read_signed_leb128(data);
						offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data));
						altstr.position((int)offset);
						macro = readString(altstr);
						if (DEBUG)
							System.out.println(" DW_MACRO_undef_indirect_alt - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$
									+ macro);
					}
					break;
					case DwarfConstants.DW_MACRO_transparent_include: {
						long offset;
						offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data));
						ArrayList<String> foundMacros = t_macros.get(Long.valueOf(offset));
						if (foundMacros != null)
							macros.addAll(foundMacros);
						else if (lt_offset >= 0) {
							macros.add(fixupMarker + offset); // leave a marker we can fix up later
							if (DEBUG)
								System.out.println("Adding fixup for offset: " + offset + " for file: " + fileName); //$NON-NLS-1$ //$NON-NLS-2$
							fixupList.add(fileName);
						}
							
						if (DEBUG)
							System.out.println(" DW_MACRO_transparent_include - offset : " + offset); //$NON-NLS-1$
					}
					break;
					case DwarfConstants.DW_MACRO_transparent_include_alt: {
						long offset;
						offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data));
						if (lt_offset >= 0) {
							macros.add("=FIXUPALT=" + offset); // leave a marker we can fix up later //$NON-NLS-1$
							if (DEBUG)
								System.out.println("Adding alt fixup for offset: " + offset + " for file: " + fileName); //$NON-NLS-1$ //$NON-NLS-2$
							fixupAltList.add(fileName);
						}
							
						if (DEBUG)
							System.out.println(" DW_MACRO_transparent_include - offset : " + offset); //$NON-NLS-1$
					}
					break;
					case DwarfConstants.DW_MACRO_end: {
						if (lt_offset < 0) {
							if (DEBUG)
								System.out.println("creating transparent include macros for offset: " + original_position); //$NON-NLS-1$
							t_macros.put(Long.valueOf(original_position), macros);
						}
						done = true;
					}
					break;
					default: {
						if (opcodeInfos != null) {
							OpcodeInfo info = opcodeInfos.get(op);
							info.readPastEntry(data);
						}
					}
					break;
					}
				}
			}  catch (IOException e) {
				// do nothing
			}
		}
	}

	/**
	 * Get the set of command line flags used for a particular file name.
	 * 
	 * @param fileName - name of file
	 * @return string containing all macros used on command line to compile the file
	 * 
	 * @since 5.7
	 */
	@Override
	public String getCompileOptions(String fileName) {
		if (!m_macros_parsed) {
			getSourceFiles();
			getCommandMacrosFromMacroSection();
			m_macros_parsed = true;
		}
		ArrayList<String>macros = m_compileOptionsMap.get(fileName);
		if (macros == null)
			return ""; //$NON-NLS-1$
		
		StringBuffer sb = new StringBuffer();
		for (String option: macros) {
			sb.append(option);
			sb.append(" "); //$NON-NLS-1$
		}
		return sb.toString();
	}

}
