diff --git a/src/main/java/net/praqma/jenkins/memorymap/MemoryMapDslExtension.java b/src/main/java/net/praqma/jenkins/memorymap/MemoryMapDslExtension.java index 3a97ebb3..0f11bb31 100644 --- a/src/main/java/net/praqma/jenkins/memorymap/MemoryMapDslExtension.java +++ b/src/main/java/net/praqma/jenkins/memorymap/MemoryMapDslExtension.java @@ -26,7 +26,7 @@ } ``` -Valid values for `parserType` are `GCC` and `TI`. Valid values for `scale` are `DEFAULT`, `KILO`, `MEGA` and `GIGA`. +Valid values for `parserType` are `GCC`, 'PowerPCEabiGcc' and `TI`. Valid values for `scale` are `DEFAULT`, `KILO`, `MEGA` and `GIGA`. ``` job ('mmap_GEN'){ diff --git a/src/main/java/net/praqma/jenkins/memorymap/MemoryMapJobDslContext.java b/src/main/java/net/praqma/jenkins/memorymap/MemoryMapJobDslContext.java index fc98bfee..b6a06dd9 100644 --- a/src/main/java/net/praqma/jenkins/memorymap/MemoryMapJobDslContext.java +++ b/src/main/java/net/praqma/jenkins/memorymap/MemoryMapJobDslContext.java @@ -10,6 +10,7 @@ import static javaposse.jobdsl.plugin.ContextExtensionPoint.executeInContext; import net.praqma.jenkins.memorymap.parser.AbstractMemoryMapParser; import net.praqma.jenkins.memorymap.parser.gcc.GccMemoryMapParser; +import net.praqma.jenkins.memorymap.parser.powerpceabigcc.PowerPCEabiGccMemoryMapParser; import net.praqma.jenkins.memorymap.parser.ti.TexasInstrumentsMemoryMapParser; public class MemoryMapJobDslContext implements Context { @@ -46,7 +47,7 @@ public void scale(String value) { } List parsers = new ArrayList<>(); - List parserTypes = Arrays.asList("GCC", "TI"); + List parserTypes = Arrays.asList("GCC", "PowerPCEabiGcc", "TI"); public void parser(String parserType, String parserUniqueName, String commandFile, String mapFile, Runnable closure) { checkArgument(parserTypes.contains(parserType), "Parser type must be one of " + parserTypes); @@ -58,6 +59,9 @@ public void parser(String parserType, String parserUniqueName, String commandFil case "GCC": parser = new GccMemoryMapParser(parserUniqueName, mapFile, commandFile, wordSize, showBytesOnGraphs, context.graphConfigurations); break; + case "PowerPCEabiGcc": + parser = new PowerPCEabiGccMemoryMapParser(parserUniqueName, mapFile, commandFile, wordSize, showBytesOnGraphs, context.graphConfigurations); + break; case "TI": parser = new TexasInstrumentsMemoryMapParser(parserUniqueName, mapFile, commandFile, wordSize, context.graphConfigurations, showBytesOnGraphs); break; diff --git a/src/main/java/net/praqma/jenkins/memorymap/parser/AbstractMemoryMapParser.java b/src/main/java/net/praqma/jenkins/memorymap/parser/AbstractMemoryMapParser.java index 1fcf5dc9..b9634c2c 100644 --- a/src/main/java/net/praqma/jenkins/memorymap/parser/AbstractMemoryMapParser.java +++ b/src/main/java/net/praqma/jenkins/memorymap/parser/AbstractMemoryMapParser.java @@ -51,6 +51,7 @@ import net.praqma.jenkins.memorymap.graph.MemoryMapGraphConfiguration; import net.praqma.jenkins.memorymap.graph.MemoryMapGraphConfigurationDescriptor; import net.praqma.jenkins.memorymap.parser.gcc.GccMemoryMapParser; +import net.praqma.jenkins.memorymap.parser.powerpceabigcc.PowerPCEabiGccMemoryMapParser; import net.praqma.jenkins.memorymap.parser.ti.TexasInstrumentsMemoryMapParser; import net.praqma.jenkins.memorymap.result.MemoryMapConfigMemory; import org.apache.commons.collections.ListUtils; @@ -63,6 +64,8 @@ @JsonSubTypes({ @Type(value = TexasInstrumentsMemoryMapParser.class, name = "TexasInstrumentsMemoryMapParser") , + @Type(value = PowerPCEabiGccMemoryMapParser.class, name = "PowerPCEabiGccMemoryMapParser") + , @Type(value = GccMemoryMapParser.class, name = "GccMemoryMapParser")}) public abstract class AbstractMemoryMapParser implements Describable, ExtensionPoint, Serializable { diff --git a/src/main/java/net/praqma/jenkins/memorymap/parser/powerpceabigcc/PowerPCEabiGccMemoryMapParser.java b/src/main/java/net/praqma/jenkins/memorymap/parser/powerpceabigcc/PowerPCEabiGccMemoryMapParser.java new file mode 100644 index 00000000..24d48143 --- /dev/null +++ b/src/main/java/net/praqma/jenkins/memorymap/parser/powerpceabigcc/PowerPCEabiGccMemoryMapParser.java @@ -0,0 +1,262 @@ +package net.praqma.jenkins.memorymap.parser.powerpceabigcc; + +import hudson.AbortException; +import hudson.Extension; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import net.praqma.jenkins.memorymap.graph.MemoryMapGraphConfiguration; +import net.praqma.jenkins.memorymap.parser.AbstractMemoryMapParser; +import net.praqma.jenkins.memorymap.parser.MemoryMapParserDescriptor; +import net.praqma.jenkins.memorymap.result.MemoryMapConfigMemory; +import net.praqma.jenkins.memorymap.result.MemoryMapConfigMemoryItem; +import net.praqma.jenkins.memorymap.util.HexUtils; +import net.praqma.jenkins.memorymap.util.HexUtils.HexifiableString; +import net.praqma.jenkins.memorymap.util.MemoryMapMemorySelectionError; +import net.sf.json.JSONObject; +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; + +/** + * @author Praqma + */ +public class PowerPCEabiGccMemoryMapParser extends AbstractMemoryMapParser implements Serializable { + + private static final Pattern MEM_SECTIONS = Pattern.compile("^\\s*(\\S+)(?=.*:)", Pattern.MULTILINE); + private static final Pattern COMMENT_BLOCKS = Pattern.compile("\\/\\*[\\s\\S]*?\\*\\/"); + private static final Logger LOG = Logger.getLogger(PowerPCEabiGccMemoryMapParser.class.getName()); + + @DataBoundConstructor + public PowerPCEabiGccMemoryMapParser(String parserUniqueName, String mapFile, String configurationFile, Integer wordSize, Boolean bytesOnGraph, List graphConfiguration) { + super(parserUniqueName, mapFile, configurationFile, wordSize, bytesOnGraph, graphConfiguration); + } + + /** + * Strip any c-style block comments, e.g slash-star to star-slash. + * Note: this function down not correctly handle nested comment + * blocks. + * Note: this function is a bit greedy, and will incorrectly strip + * comments inside strings, but this shouldn't be a problem for memory + * config files. + * + * @param seq The content of a file that might contain c-style + * block-comments + * @return The same content, that has now had all block-comments stripped + * out. + */ + public static CharSequence stripComments(CharSequence seq) { + Matcher commentMatcher = COMMENT_BLOCKS.matcher(seq); + return commentMatcher.replaceAll(""); + } + + /** + * Parses the MEMORY section of the GCC file. Throws an abort exception + * which will be shown in the Jenkins console log. + * + * @param seq The content of the map file + * @return a list of the defined MEMORY in the map file + * @throws hudson.AbortException when a illegal value of memory found + * + */ + public MemoryMapConfigMemory getMemory(CharSequence seq) throws AbortException { + + Pattern allMemory = Pattern.compile("^\\s*(\\S+).*?(?:ORIGIN|org|o)\\s*=\\s*\\(([^,]*)\\).*?(?:LENGTH|len|l)\\s*\\=\\s*([^\\s]*.*)", Pattern.MULTILINE); + Matcher match = allMemory.matcher(seq); + MemoryMapConfigMemory memory = new MemoryMapConfigMemory(); + while (match.find()) { + String sectionName = match.group(1); + String sectionAddress = match.group(2).replace("(", "").replace(")", "").replace(" ", ""); + String sectionLength = match.group(3).replace("(", "").replace(")", "").replace(" ", ""); + + sectionAddress = getHexSum(sectionAddress); + sectionLength = getHexSum(sectionLength); + + try { + String hexLength = new HexUtils.HexifiableString(sectionLength).toValidHexString().rawString; + MemoryMapConfigMemoryItem item = new MemoryMapConfigMemoryItem(sectionName, sectionAddress, hexLength); + memory.add(item); + } catch (Throwable ex) { + logger.log(Level.SEVERE, "Unable to convert %s to a valid hex string.", ex); + throw new AbortException(String.format("Unable to convert %s to a valid hex string.", sectionLength)); + } + } + return memory; + } + + private static String getHexSum(String group) + { + Long sum = Long.valueOf(0L); + + String buff = group.replace("0x", ""); + try + { + String[] groupPluses = buff.split("\\+"); + if (groupPluses.length > 0) { + for (String str1 : groupPluses) + { + String[] minusGroup = str1.split("\\-"); + if (minusGroup.length > 0) { + for (int i = 0; i < minusGroup.length; i++) { + if (i == 0) { + sum = Long.valueOf(sum.longValue() + Long.parseLong(minusGroup[i], 16)); + } else { + sum = Long.valueOf(sum.longValue() - Long.parseLong(minusGroup[i], 16)); + } + } + } else { + sum = Long.valueOf(sum.longValue() + Long.parseLong(str1, 16)); + } + } + } else { + sum = Long.valueOf(sum.longValue() + Long.parseLong(group, 16)); + } + } + catch (NumberFormatException ex) + { + return group; + } + return "0x" + Long.toHexString(sum.longValue()); + } + + /* + * SECTIONS { + * --- + * secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) + { contents } >region =fill + ... + } + * + */ + public List getSections(CharSequence m) { + List items = new ArrayList<>(); + + Pattern section = Pattern.compile("SECTIONS\\s?\\r?\\n?\\{([\\s\\S]*)\\n\\}", Pattern.MULTILINE); + + Matcher sectionMatched = section.matcher(m); + String sectionString = null; + + while (sectionMatched.find()) { + sectionString = sectionMatched.group(1); + } + + //Find the good stuff (SECTION): *SECTIONS\n\{(.*)\n\} + Matcher fm = MEM_SECTIONS.matcher(sectionString); + + while (fm.find()) { + MemoryMapConfigMemoryItem it = new MemoryMapConfigMemoryItem(fm.group(1), "0"); + items.add(it); + } + return items; + } + + public PowerPCEabiGccMemoryMapParser() { + super(); + } + + public Pattern getLinePatternForMapFile(String sectionName) { + return Pattern.compile(String.format("^(%s)(\\s+)(\\w+)(\\s+)(\\w+)(\\w*)", sectionName), Pattern.MULTILINE); + } + + private static class MemoryMapMemItemComparator implements Comparator, Serializable { + @Override + public int compare(MemoryMapConfigMemoryItem t, MemoryMapConfigMemoryItem t1) { + long vt = new HexifiableString(t.getOrigin()).getLongValue(); + long vt1 = new HexifiableString(t1.getOrigin()).getLongValue(); + return (vt < vt1 ? -1 : (vt == vt1 ? 1 : 0)); + } + } + + /** + * Given an item with length == null. Look down in the list. If we find an + * item whose length is not null, set the items length to that + * + * @param memory the memory list + * @return a more complete configuration, where i have better values + */ + public MemoryMapConfigMemory guessLengthOfSections(MemoryMapConfigMemory memory) { + Collections.sort(memory, new MemoryMapMemItemComparator()); + + for (MemoryMapConfigMemoryItem item : memory) { + if (item.getLength() == null) { + int itemIndex = memory.indexOf(item); + for (int i = itemIndex; i > 1; i--) { + if (memory.get(i).getLength() != null) { + item.setParent(memory.get(i)); + break; + } + } + + } + } + return memory; + } + + @Override + public MemoryMapConfigMemory parseMapFile(File f, MemoryMapConfigMemory configuration) throws IOException { + CharSequence sequence = createCharSequenceFromFile(f); + + for (MemoryMapConfigMemoryItem item : configuration) { + Matcher m = getLinePatternForMapFile(item.getName()).matcher(sequence); + while (m.find()) { + item.setOrigin(m.group(3)); + item.setUsed(m.group(5)); + } + } + + configuration = guessLengthOfSections(configuration); + + return configuration; + } + + @Override + public MemoryMapConfigMemory parseConfigFile(File f) throws IOException { + //Collect sections from both the MEMORY and the SECTIONS areas from the command file. + //The memory are the top level components, sections belong to one of these sections + CharSequence stripped = stripComments(createCharSequenceFromFile(f)); + + MemoryMapConfigMemory memConfig = getMemory(stripped); + memConfig.addAll(getSections(stripped)); + for (MemoryMapGraphConfiguration g : getGraphConfiguration()) { + for (String gItem : g.itemizeGraphDataList()) { + for (String gSplitItem : gItem.split("\\+")) { + //We will fail if the name of the data section does not match any of the named items in the map file. + if (!memConfig.containsSectionWithName(gSplitItem)) { + throw new MemoryMapMemorySelectionError(String.format("The memory section named %s not found in map file%nAvailable sections are:%n%s", gSplitItem, memConfig.getItemNames())); + } + } + } + } + return memConfig; + } + + @Override + public int getDefaultWordSize() { + return 8; + } + + @Symbol("powerPCEabiGccParser") + @Extension + public static final class DescriptorImpl extends MemoryMapParserDescriptor { + + @Override + public String getDisplayName() { + return "PowerPCEabiGcc"; + } + + @Override + public AbstractMemoryMapParser newInstance(StaplerRequest req, JSONObject formData, AbstractMemoryMapParser instance) throws FormException { + PowerPCEabiGccMemoryMapParser parser = (PowerPCEabiGccMemoryMapParser) instance; + save(); + return parser; + } + } +} diff --git a/src/main/resources/net/praqma/jenkins/memorymap/parser/powerpceabigcc/PowerPCEabiGccMemoryMapParser/config.jelly b/src/main/resources/net/praqma/jenkins/memorymap/parser/powerpceabigcc/PowerPCEabiGccMemoryMapParser/config.jelly new file mode 100644 index 00000000..ca249d9b --- /dev/null +++ b/src/main/resources/net/praqma/jenkins/memorymap/parser/powerpceabigcc/PowerPCEabiGccMemoryMapParser/config.jelly @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/test/java/net/praqma/jenkins/integration/PowerPCEabiGccMemoryMapParserIT.java b/src/test/java/net/praqma/jenkins/integration/PowerPCEabiGccMemoryMapParserIT.java new file mode 100644 index 00000000..d65f0250 --- /dev/null +++ b/src/test/java/net/praqma/jenkins/integration/PowerPCEabiGccMemoryMapParserIT.java @@ -0,0 +1,61 @@ +/* + * The MIT License + * + * Copyright 2019 Praqma. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package net.praqma.jenkins.integration; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.UUID; +import net.praqma.jenkins.memorymap.graph.MemoryMapGraphConfiguration; +import net.praqma.jenkins.memorymap.parser.powerpceabigcc.PowerPCEabiGccMemoryMapParser; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +/** + * + * @author thi + */ +public class PowerPCEabiGccMemoryMapParserIT { + + @Rule + public JenkinsRule jenkins = new JenkinsRule(); + + @Test + public void powerpceabigcc_testUsageValues() throws Exception { + MemoryMapGraphConfiguration graphConfiguration = new MemoryMapGraphConfiguration(".text+.rodata", "432"); + PowerPCEabiGccMemoryMapParser parser = createParser(graphConfiguration); + parser.setMapFile("prom.map"); + parser.setConfigurationFile("prom.ld"); + + HashMap expectedValues = new HashMap<>(); + expectedValues.put(".text", "0x10a008"); + expectedValues.put(".rodata", "0x33fd7"); + + TestUtils.testUsageValues(jenkins, parser, "powerpceabigcc.zip", expectedValues); + } + + private PowerPCEabiGccMemoryMapParser createParser(MemoryMapGraphConfiguration... graphConfiguration) { + return new PowerPCEabiGccMemoryMapParser(UUID.randomUUID().toString(), null, null, 8, true, Arrays.asList(graphConfiguration)); + } +} \ No newline at end of file diff --git a/src/test/resources/net/praqma/jenkins/integration/powerpceabigcc.zip b/src/test/resources/net/praqma/jenkins/integration/powerpceabigcc.zip new file mode 100644 index 00000000..bd3b5c24 Binary files /dev/null and b/src/test/resources/net/praqma/jenkins/integration/powerpceabigcc.zip differ