eventlog2html 0.10.0 → 0.11.0
raw patch · 25 files changed
+504/−280 lines, 25 filesdep ~aesondep ~blaze-htmldep ~bytestringnew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, blaze-html, bytestring, containers, filepath, mtl, optparse-applicative, text
API changes (from Hackage documentation)
- Eventlog.Data: HeapProfile :: (Header, Value, Maybe Value, Maybe Html) -> EventlogType
- Eventlog.Data: TickyProfile :: (Header, Word64, Double, Html) -> EventlogType
- Eventlog.HtmlTemplate: NoTraceData :: IncludeTraceData
- Eventlog.HtmlTemplate: TraceData :: IncludeTraceData
- Eventlog.HtmlTemplate: data IncludeTraceData
- Eventlog.HtmlTemplate: type VizID = Int
- Eventlog.Ticky: AccumStats :: !Word64 -> !Word64 -> !Word64 -> ![(Double, Word64, Word64)] -> AccumStats
- Eventlog.Ticky: [allocd] :: AccumStats -> !Word64
- Eventlog.Ticky: [allocs] :: AccumStats -> !Word64
- Eventlog.Ticky: [entries] :: AccumStats -> !Word64
- Eventlog.Ticky: [series] :: AccumStats -> ![(Double, Word64, Word64)]
- Eventlog.Ticky: accumulateSamples :: [TickySample] -> Map TickyCounterId AccumStats
- Eventlog.Ticky: closureSize :: Int -> Int -> Int
- Eventlog.Ticky: css :: AttributeValue -> Html
- Eventlog.Ticky: data AccumStats
- Eventlog.Ticky: emptyAccumStats :: AccumStats
- Eventlog.Ticky: htmlHeader :: Args -> Html
- Eventlog.Ticky: initStats :: TickySample -> AccumStats
- Eventlog.Ticky: initTable :: Bool -> Text
- Eventlog.Ticky: insertSample :: TickySample -> AccumStats -> AccumStats
- Eventlog.Ticky: jsScript :: String -> Html
- Eventlog.Ticky: render :: Fixed E2 -> String
- Eventlog.Ticky: renderSpark :: Int -> [(Double, Word64, Word64)] -> Html
- Eventlog.Ticky: renderTickyInfo :: Bool -> Map TickyCounterId (InfoTableLocStatus, (TickyCounter, AccumStats, Double)) -> Html
- Eventlog.Ticky: template :: Header -> Word64 -> Double -> Html -> Args -> Html
- Eventlog.Ticky: tickyTemplateString :: Header -> Word64 -> Double -> Html -> Args -> String
- Eventlog.Ticky: trunc :: Double -> Fixed E2
+ Eventlog.Args: Number :: Sort
+ Eventlog.AssetVersions: popperURL :: String
+ Eventlog.AssetVersions: popperVersion :: String
+ Eventlog.Data: EventlogType :: Header -> Maybe HeapProfileData -> Maybe TickyProfileData -> EventlogType
+ Eventlog.Data: HeapProfileData :: Value -> Maybe Value -> Maybe Html -> HeapProfileData
+ Eventlog.Data: TickyProfileData :: Word64 -> Double -> Html -> TickyProfileData
+ Eventlog.Data: [eventlogHeader] :: EventlogType -> Header
+ Eventlog.Data: [eventlogHeapProfile] :: EventlogType -> Maybe HeapProfileData
+ Eventlog.Data: [eventlogTickyProfile] :: EventlogType -> Maybe TickyProfileData
+ Eventlog.Data: data HeapProfileData
+ Eventlog.Data: data TickyProfileData
+ Eventlog.HtmlTemplate: allHeapTabs :: Header -> Args -> HeapProfileData -> [TabGroup]
+ Eventlog.HtmlTemplate: allTabs :: EventlogType -> Args -> [TabGroup]
+ Eventlog.HtmlTemplate: costCentresTab :: Args -> HeapProfileData -> TabGroup
+ Eventlog.HtmlTemplate: detailedTab :: HeapProfileData -> TabGroup
+ Eventlog.HtmlTemplate: has_heap_profile :: Header -> Bool
+ Eventlog.HtmlTemplate: heapDocs :: Html
+ Eventlog.HtmlTemplate: heapProfileTabs :: Header -> Args -> HeapProfileData -> TabGroup
+ Eventlog.HtmlTemplate: heapTab :: Args -> TabGroup
+ Eventlog.HtmlTemplate: metaTab :: Header -> Args -> Tab
+ Eventlog.HtmlTemplate: mk :: Args -> ChartType -> TabID -> Html
+ Eventlog.HtmlTemplate: noCostCentresDocs :: Html
+ Eventlog.HtmlTemplate: noDetailedDocs :: Html
+ Eventlog.HtmlTemplate: noHeapProfileDocs :: Html
+ Eventlog.HtmlTemplate: noHeapProfileTab :: Tab
+ Eventlog.HtmlTemplate: noTickyDocs :: Html
+ Eventlog.HtmlTemplate: perTabFooter :: Header -> Html
+ Eventlog.HtmlTemplate: renderTab :: Header -> Tab -> Html
+ Eventlog.HtmlTemplate: tickyProfileTabs :: Maybe TickyProfileData -> TabGroup
+ Eventlog.Javascript: popper :: Text
+ Eventlog.Rendering.Bootstrap: ariaControls :: AttributeValue -> Attribute
+ Eventlog.Rendering.Bootstrap: ariaExpanded :: AttributeValue -> Attribute
+ Eventlog.Rendering.Bootstrap: ariaLabel :: AttributeValue -> Attribute
+ Eventlog.Rendering.Bootstrap: dataTarget :: AttributeValue -> Attribute
+ Eventlog.Rendering.Bootstrap: dataToggle :: AttributeValue -> Attribute
+ Eventlog.Rendering.Bootstrap: navbar :: [TabGroup] -> Html
+ Eventlog.Rendering.Bootstrap: tabClasses :: Tab -> AttributeValue
+ Eventlog.Rendering.Types: ManyTabs :: String -> [Tab] -> TabGroup
+ Eventlog.Rendering.Types: NoTraceData :: IncludeTraceData
+ Eventlog.Rendering.Types: SingleTab :: Tab -> TabGroup
+ Eventlog.Rendering.Types: Tab :: String -> TabID -> Maybe Html -> Maybe Html -> Bool -> Bool -> Tab
+ Eventlog.Rendering.Types: TabID :: Text -> TabID
+ Eventlog.Rendering.Types: TraceData :: IncludeTraceData
+ Eventlog.Rendering.Types: [tabActive] :: Tab -> Bool
+ Eventlog.Rendering.Types: [tabContent] :: Tab -> Maybe Html
+ Eventlog.Rendering.Types: [tabDisabled] :: Tab -> Bool
+ Eventlog.Rendering.Types: [tabDocs] :: Tab -> Maybe Html
+ Eventlog.Rendering.Types: [tabId] :: Tab -> TabID
+ Eventlog.Rendering.Types: [tabName] :: Tab -> String
+ Eventlog.Rendering.Types: data IncludeTraceData
+ Eventlog.Rendering.Types: data Tab
+ Eventlog.Rendering.Types: data TabGroup
+ Eventlog.Rendering.Types: instance Data.String.IsString Eventlog.Rendering.Types.TabID
+ Eventlog.Rendering.Types: mkOptionalTab :: String -> TabID -> (a -> Html) -> Maybe Html -> Html -> Maybe a -> Tab
+ Eventlog.Rendering.Types: mkTab :: String -> TabID -> Html -> Maybe Html -> Tab
+ Eventlog.Rendering.Types: mkTabID :: String -> TabID
+ Eventlog.Rendering.Types: mkUnavailableTab :: String -> TabID -> Html -> Tab
+ Eventlog.Rendering.Types: newtype TabID
+ Eventlog.Rendering.Types: noDocs :: Maybe Html
+ Eventlog.Rendering.Types: tabIDToHref :: TabID -> Text
+ Eventlog.Rendering.Types: tabIDToNavItemID :: TabID -> Text
+ Eventlog.Rendering.Types: tabIDToTabID :: TabID -> Text
+ Eventlog.Rendering.Types: tabIDToVizID :: TabID -> Text
+ Eventlog.Ticky: tickyTab :: TickyProfileData -> Html
+ Eventlog.Types: EventlogType :: Header -> Maybe HeapProfileData -> Maybe TickyProfileData -> EventlogType
+ Eventlog.Types: HeapProfileData :: Value -> Maybe Value -> Maybe Html -> HeapProfileData
+ Eventlog.Types: TickyProfileData :: Word64 -> Double -> Html -> TickyProfileData
+ Eventlog.Types: [eventlogHeader] :: EventlogType -> Header
+ Eventlog.Types: [eventlogHeapProfile] :: EventlogType -> Maybe HeapProfileData
+ Eventlog.Types: [eventlogTickyProfile] :: EventlogType -> Maybe TickyProfileData
+ Eventlog.Types: data EventlogType
+ Eventlog.Types: data HeapProfileData
+ Eventlog.Types: data TickyProfileData
+ Eventlog.Types: toItblPointer_either :: Bucket -> Either String InfoTablePtr
+ Eventlog.Types: toItblPointer_maybe :: Bucket -> Maybe InfoTablePtr
- Eventlog.Data: generateJsonData :: Args -> ProfData -> IO (Header, Value, Maybe Value, Maybe Html)
+ Eventlog.Data: generateJsonData :: Args -> ProfData -> HeapProfileData
- Eventlog.HtmlTemplate: encloseRawVegaScript :: VizID -> Text -> Html
+ Eventlog.HtmlTemplate: encloseRawVegaScript :: TabID -> Text -> Html
- Eventlog.HtmlTemplate: encloseScript :: [Text] -> VizID -> Text -> Html
+ Eventlog.HtmlTemplate: encloseScript :: [Text] -> TabID -> Text -> Html
- Eventlog.HtmlTemplate: encloseScriptX :: [Text] -> VizID -> Text -> Html
+ Eventlog.HtmlTemplate: encloseScriptX :: [Text] -> TabID -> Text -> Html
- Eventlog.HtmlTemplate: htmlHeader :: Value -> Maybe Value -> Args -> Html
+ Eventlog.HtmlTemplate: htmlHeader :: Maybe HeapProfileData -> Maybe TickyProfileData -> Args -> Html
- Eventlog.HtmlTemplate: renderChart :: IncludeTraceData -> ChartType -> Bool -> VizID -> Text -> Html
+ Eventlog.HtmlTemplate: renderChart :: IncludeTraceData -> ChartType -> Bool -> TabID -> Text -> Html
- Eventlog.HtmlTemplate: renderChartWithJson :: IncludeTraceData -> ChartType -> Int -> Value -> Text -> Html
+ Eventlog.HtmlTemplate: renderChartWithJson :: IncludeTraceData -> ChartType -> TabID -> Value -> Text -> Html
- Eventlog.HtmlTemplate: template :: Header -> Value -> Maybe Value -> Maybe Html -> Args -> Html
+ Eventlog.HtmlTemplate: template :: EventlogType -> Args -> [TabGroup] -> Html
- Eventlog.HtmlTemplate: templateString :: Header -> Value -> Maybe Value -> Maybe Html -> Args -> String
+ Eventlog.HtmlTemplate: templateString :: EventlogType -> Args -> String
- Eventlog.Types: data ClosureType
+ Eventlog.Types: data () => ClosureType
- Eventlog.Types: data HeapProfBreakdown
+ Eventlog.Types: data () => HeapProfBreakdown
- Eventlog.Vega: bandsToVega :: Map Bucket (Int, BucketInfo) -> (UArray Int Double, UArray (Int, Int) Double) -> [VegaEntry]
+ Eventlog.Vega: bandsToVega :: (BucketInfo -> Text) -> Map Bucket (Int, BucketInfo) -> (UArray Int Double, UArray (Int, Int) Double) -> [VegaEntry]
Files
- CHANGELOG +8/−1
- eventlog2html.cabal +13/−9
- inline-docs/heap.html +31/−0
- inline-docs/no-cost-centres.html +3/−0
- inline-docs/no-detailed.html +3/−0
- inline-docs/no-heap-profile.html +14/−0
- inline-docs/no-ticky.html +3/−0
- javascript/generated/fancyTable.min.js +2/−2
- javascript/generated/popper.js@1.14.3 +5/−0
- javascript/stylesheet.css +13/−3
- javascript/tablogic.js +5/−26
- main/Main.hs +2/−7
- src/Eventlog/Args.hs +4/−3
- src/Eventlog/AssetVersions.hs +3/−0
- src/Eventlog/Data.hs +35/−24
- src/Eventlog/Detailed.hs +1/−2
- src/Eventlog/HtmlTemplate.hs +180/−81
- src/Eventlog/Javascript.hs +5/−0
- src/Eventlog/Prune.hs +9/−0
- src/Eventlog/Rendering/Bootstrap.hs +65/−0
- src/Eventlog/Rendering/Types.hs +65/−0
- src/Eventlog/Ticky.hs +6/−110
- src/Eventlog/Types.hs +23/−4
- src/Eventlog/Vega.hs +4/−3
- src/Eventlog/VegaTemplate.hs +2/−5
CHANGELOG view
@@ -1,3 +1,10 @@+0.11.0 release 2024-02-06++* Add sorting by numbers [#183](https://github.com/mpickering/eventlog2html/pull/183)+* Refactoring and modernization of the UI [#178](https://github.com/mpickering/eventlog2html/pull/178)+* Compatibility with 9.0, 9.2, 9.4, 9.6 and 9.8 which are now also checked in CI.+ ([#180](https://github.com/mpickering/eventlog2html/pull/180) and [#181](https://github.com/mpickering/eventlog2html/pull/181))+ 0.10.0 release 2023-03-31 * Add support for rendering ticky profiles@@ -6,7 +13,7 @@ * Add "--version" flag for eventlog2html which prints the version, git commit and git branch used to build eventlog2html.-* Compatability with 9.4.* and 9.2.* compilers.+* Compatibility with 9.4.* and 9.2.* compilers. 0.9.2 release 2021-11-24
eventlog2html.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 Name: eventlog2html-Version: 0.10.0+Version: 0.11.0 Synopsis: Visualise an eventlog Description: eventlog2html is a library for visualising eventlogs. At the moment, the intended use is to visualise eventlogs@@ -28,29 +28,31 @@ javascript/generated/vega-embed@6.14.0 javascript/generated/vega@5.17.0 javascript/generated/vega-lite@4.17.0+ javascript/generated/popper.js@1.14.3+ inline-docs/*.html extra-doc-files: README.md CHANGELOG-Tested-With: GHC ==9.2.5, GHC ==9.4.3+Tested-With: GHC ==9.0.2, GHC ==9.2.7, GHC ==9.4.7, GHC ==9.6.2, GHC ==9.8.1 Library Build-depends:- aeson >= 1.4.3 && < 1.6 || >= 2.0 && < 2.2,+ aeson >= 1.4.3 && < 1.6 || >= 2.0 && < 2.3, attoparsec >= 0.13.2 && < 0.15, array >= 0.5.3 && < 0.6, base >= 4 && < 5, blaze-html >= 0.9.1 && < 0.10,- bytestring >= 0.10.8 && < 0.12,+ bytestring >= 0.10.8 && < 0.13, blaze-markup,- containers >= 0.5.0 && < 0.7,+ containers >= 0.5.0 && < 0.8, file-embed >= 0.0.11 && < 0.1,- filepath >= 1.4.2 && < 1.5,+ filepath >= 1.4.2 && < 1.6, ghc-events >= 0.19.0 && < 0.20, hashtables >= 1.2.3 && < 1.4, hvega >= 0.11.0 && < 0.13,- mtl >= 2.2.2 && < 2.3,- optparse-applicative >= 0.14.3 && < 0.18,+ mtl >= 2.2.2 && < 2.4,+ optparse-applicative >= 0.14.3 && < 0.19, semigroups >= 0.18 && < 0.21,- text >= 1.2.3 && < 1.3 || >= 2.0 && < 2.1,+ text >= 1.2.3 && < 1.3 || >= 2.0 && < 2.2, time >= 1.8.0 && < 2.0, vector >= 0.11, trie-simple >= 0.4,@@ -74,6 +76,8 @@ Eventlog.HeapProf Eventlog.Vega Eventlog.HtmlTemplate+ Eventlog.Rendering.Bootstrap+ Eventlog.Rendering.Types Eventlog.VegaTemplate Eventlog.AssetVersions Eventlog.Detailed
+ inline-docs/heap.html view
@@ -0,0 +1,31 @@+<dl>+ <dt style="color: red">Heap Size</dt>+ <dd>Total size of <emph>megablocks</emph> allocated by the RTS</dd>+ <dt style="color: blue">Blocks Size</dt>+ <dd>Total size of <emph>blocks</emph> allocated by the RTS</dd>+ <dt style="color: green">Live bytes</dt>+ <dd>Total size of objects live on the heap (should match top of area pane)</dd>+</dl>++<p>Process memory usage (as reported by the OS) should correspond roughly to the <span style="color: red">heap size</span>,+except for data allocated through the FFI (and the RTS also calls <tt>malloc()</tt> itself in a few places).+</p>++<p>+When heap profiling is enabled, <span style="color: blue">blocks+size</span> and <span style="color: green">live bytes</span>+will typically differ by the size of the nursery.+For understanding the relationship between <span style="color: blue">blocks+size</span> and <span style="color: green">live bytes</span>,+this <a href="https://www.well-typed.com/blog/2021/03/memory-return/">blog+post</a> contains more information.</p>++<p>+When heap profiling is not enabled, <span style="color: green">live bytes</span> will be updated only on a major GC, but <span style="color: red">heap size</span> and <span style="color: blue">blocks size</span> are updated more frequently. (Enabling heap profiling causes more frequent major GCs to collect heap samples.)+</p>++<p>+This view can also be useful in identifying+fragmentation. A very badly fragmented heap will have low <span style="color: green">live bytes</span> but+much higher <span style="color: blue">blocks size</span>.+</p>
+ inline-docs/no-cost-centres.html view
@@ -0,0 +1,3 @@+<p>+The cost centres view is available only when executing the program with <tt>+RTS -hc</tt>.+</p>
+ inline-docs/no-detailed.html view
@@ -0,0 +1,3 @@+<p>+No detailed data to display.+</p>
+ inline-docs/no-heap-profile.html view
@@ -0,0 +1,14 @@+<p>+This eventlog was generated without heap profiling.+</p>++<p>+To create a heap profile, you need to run the program with one of the <tt>+RTS+-h...</tt> options. The <tt>-hT</tt> (closure type) and <tt>-hi</tt> (info+table) heap profiling options are available for all programs. If you compile+with profiling enabled, more options are available.+</p>++<p>+See the <a href="https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html#profiling-memory-usage">GHC user's guide on profiling memory usage</a> for more information.+</p>
+ inline-docs/no-ticky.html view
@@ -0,0 +1,3 @@+<p>+No ticky data to display.+</p>
javascript/generated/fancyTable.min.js view
@@ -1,9 +1,9 @@ /*!- * jQuery fancyTable plugin v1.0.26+ * jQuery fancyTable plugin v1.0.34 * https://github.com/myspace-nu * * Copyright 2018 Johan Johansson * Released under the MIT license */ -!function(l){l.fn.fancyTable=function(a){var s=l.extend({inputStyle:"",inputPlaceholder:"Search...",pagination:!1,paginationClass:"btn btn-light",paginationClassActive:"active",pagClosest:3,perPage:10,sortable:!0,searchable:!0,matchCase:!1,exactMatch:!1,onInit:function(){},onUpdate:function(){},sortFunction:function(a,e,t){return"numeric"==t.sortAs[t.sortColumn]?0<t.sortOrder?parseFloat(a)-parseFloat(e):parseFloat(e)-parseFloat(a):a<e?-t.sortOrder:e<a?t.sortOrder:0},testing:!1},a),o=this;return this.settings=s,this.tableUpdate=function(n){if(n.fancyTable.matches=0,l(n).find("tbody tr").each(function(){var a=0,e=!0,t=!1;l(this).find("td").each(function(){s.globalSearch||!n.fancyTable.searchArr[a]||o.isSearchMatch(l(this).html(),n.fancyTable.searchArr[a])?!s.globalSearch||n.fancyTable.search&&!o.isSearchMatch(l(this).html(),n.fancyTable.search)||Array.isArray(s.globalSearchExcludeColumns)&&s.globalSearchExcludeColumns.includes(a+1)||(t=!0):e=!1,a++}),s.globalSearch&&t||!s.globalSearch&&e?(n.fancyTable.matches++,!s.pagination||n.fancyTable.matches>n.fancyTable.perPage*(n.fancyTable.page-1)&&n.fancyTable.matches<=n.fancyTable.perPage*n.fancyTable.page?l(this).show():l(this).hide()):l(this).hide()}),n.fancyTable.pages=Math.ceil(n.fancyTable.matches/n.fancyTable.perPage),s.pagination){var a=n.fancyTable.paginationElement?l(n.fancyTable.paginationElement):l(n).find(".pag");a.empty();for(var e,t=1;t<=n.fancyTable.pages;t++)(1==t||t>n.fancyTable.page-(s.pagClosest+1)&&t<n.fancyTable.page+(s.pagClosest+1)||t==n.fancyTable.pages)&&(e=l("<a>",{html:t,"data-n":t,style:"margin:0.2em",class:s.paginationClass+" "+(t==n.fancyTable.page?s.paginationClassActive:"")}).css("cursor","pointer").bind("click",function(){n.fancyTable.page=l(this).data("n"),o.tableUpdate(n)}),t==n.fancyTable.pages&&n.fancyTable.page<n.fancyTable.pages-s.pagClosest-1&&a.append(l("<span>...</span>")),a.append(e),1==t&&n.fancyTable.page>s.pagClosest+2&&a.append(l("<span>...</span>")))}s.onUpdate.call(this,n)},this.isSearchMatch=function(a,e){s.matchCase||(a=a.toUpperCase(),e=e.toUpperCase());var t=s.exactMatch;return"auto"==t&&e.match(/^\".*?\"$/)?(t=!0,e=e.substring(1,e.length-1)):t=!1,t?a==e:new RegExp(e).test(a)},this.reinit=function(a){l(this).each(function(){l(this).find("th a").contents().unwrap(),l(this).find("tr.fancySearchRow").remove()}),l(this).fancyTable(this.settings)},this.tableSort=function(t){var a,e;void 0!==t.fancyTable.sortColumn&&t.fancyTable.sortColumn<t.fancyTable.nColumns&&(a=0,l(t).find("thead th").each(function(){l(this).attr("aria-sort",a==t.fancyTable.sortColumn?1==t.fancyTable.sortOrder?"ascending":-1==t.fancyTable.sortOrder?"descending":"other":null),a++}),l(t).find("thead th div.sortArrow").each(function(){l(this).remove()}),(e=l("<div>",{class:"sortArrow"}).css({margin:"0.1em",display:"inline-block",width:0,height:0,"border-left":"0.4em solid transparent","border-right":"0.4em solid transparent"})).css(0<t.fancyTable.sortOrder?{"border-top":"0.4em solid #000"}:{"border-bottom":"0.4em solid #000"}),l(t).find("thead th a").eq(t.fancyTable.sortColumn).append(e),e=l(t).find("tbody tr").toArray().sort(function(a,e){a=l(a).find("td").eq(t.fancyTable.sortColumn),e=l(e).find("td").eq(t.fancyTable.sortColumn),a=l(a).data("sortvalue")?l(a).data("sortvalue"):a.html(),e=l(e).data("sortvalue")?l(e).data("sortvalue"):e.html();return"case-insensitive"==t.fancyTable.sortAs[t.fancyTable.sortColumn]&&(a=a.toLowerCase(),e=e.toLowerCase()),s.sortFunction.call(this,a,e,t.fancyTable)}),l(t).find("tbody").empty().append(e))},this.each(function(){if("TABLE"!==l(this).prop("tagName"))return console.warn("fancyTable: Element is not a table."),!0;var t,a,e,n,r=this;r.fancyTable={nColumns:l(r).find("td").first().parent().find("td").length,nRows:l(this).find("tbody tr").length,perPage:s.perPage,page:1,pages:0,matches:0,searchArr:[],search:"",sortColumn:s.sortColumn,sortOrder:void 0!==s.sortOrder&&(new RegExp("desc","i").test(s.sortOrder)||-1==s.sortOrder)?-1:1,sortAs:[],paginationElement:s.paginationElement},0==l(r).find("tbody").length&&(e=l(r).html(),l(r).empty(),l(r).append("<tbody>").append(l(e))),0==l(r).find("thead").length&&l(r).prepend(l("<thead>")),s.sortable&&(n=0,l(r).find("thead th").each(function(){r.fancyTable.sortAs.push("numeric"==l(this).data("sortas")?"numeric":"case-insensitive"==l(this).data("sortas")?"case-insensitive":null);var a=l(this).html(),a=l("<a>",{href:"#","aria-label":"Sort by "+l(this).text(),html:a,"data-n":n,class:""}).css({cursor:"pointer",color:"inherit","text-decoration":"none"}).bind("click",function(){return r.fancyTable.sortColumn==l(this).data("n")?r.fancyTable.sortOrder=-r.fancyTable.sortOrder:r.fancyTable.sortOrder=1,r.fancyTable.sortColumn=l(this).data("n"),o.tableSort(r),o.tableUpdate(r),!1});l(this).empty(),l(this).append(a),n++})),s.searchable&&(t=l("<tr>").addClass("fancySearchRow"),s.globalSearch?(a=l("<input>",{"aria-label":"Search table",placeholder:s.inputPlaceholder,style:"width:100%;box-sizing:border-box;"+s.inputStyle}).bind("change paste keyup",function(){r.fancyTable.search=l(this).val(),r.fancyTable.page=1,o.tableUpdate(r)}),e=l("<th>",{style:"padding:2px;"}).attr("colspan",r.fancyTable.nColumns),l(a).appendTo(l(e)),l(e).appendTo(l(t))):(n=0,l(r).find("td").first().parent().find("td").each(function(){r.fancyTable.searchArr.push("");var a=l("<input>",{"aria-label":"Search column","data-n":n,placeholder:s.inputPlaceholder,style:"width:100%;box-sizing:border-box;"+s.inputStyle}).bind("change paste keyup",function(){r.fancyTable.searchArr[l(this).data("n")]=l(this).val(),r.fancyTable.page=1,o.tableUpdate(r)}),e=l("<th>",{style:"padding:2px;"});l(a).appendTo(l(e)),l(e).appendTo(l(t)),n++})),t.appendTo(l(r).find("thead"))),o.tableSort(r),s.pagination&&!s.paginationElement&&(l(r).find("tfoot").remove(),l(r).append(l("<tfoot><tr></tr></tfoot>")),l(r).find("tfoot tr").append(l("<td class='pag'></td>",{}).attr("colspan",r.fancyTable.nColumns))),o.tableUpdate(r),s.onInit.call(this,r)}),this}}(jQuery);+!function(i){i.fn.fancyTable=function(a){var o=i.extend({inputStyle:"",inputPlaceholder:"Search...",pagination:!1,paginationClass:"btn btn-light",paginationClassActive:"active",pagClosest:3,perPage:10,sortable:!0,searchable:!0,matchCase:!1,exactMatch:!1,localeCompare:!1,onInit:function(){},beforeUpdate:function(){},onUpdate:function(){},sortFunction:function(a,e,t,n,r){return a==e&&n&&r?t.rowSortOrder[i(n).data("rowid")]>t.rowSortOrder[i(r).data("rowid")]:"numeric"==t.sortAs[t.sortColumn]?0<t.sortOrder?(parseFloat(a)||0)-(parseFloat(e)||0):(parseFloat(e)||0)-(parseFloat(a)||0):o.localeCompare?a.localeCompare(e)<0?-t.sortOrder:0<a.localeCompare(e)?t.sortOrder:0:a<e?-t.sortOrder:e<a?t.sortOrder:0},testing:!1},a),l=this;return this.settings=o,this.tableUpdate=function(n){if(o.beforeUpdate.call(this,n),n.fancyTable.matches=0,i(n).find("tbody tr").each(function(){var a=0,e=!0,t=!1;i(this).find("td").each(function(){o.globalSearch||!n.fancyTable.searchArr[a]||l.isSearchMatch(i(this).html(),n.fancyTable.searchArr[a])?!o.globalSearch||n.fancyTable.search&&!l.isSearchMatch(i(this).html(),n.fancyTable.search)||Array.isArray(o.globalSearchExcludeColumns)&&o.globalSearchExcludeColumns.includes(a+1)||(t=!0):e=!1,a++}),o.globalSearch&&t||!o.globalSearch&&e?(n.fancyTable.matches++,!o.pagination||n.fancyTable.matches>n.fancyTable.perPage*(n.fancyTable.page-1)&&n.fancyTable.matches<=n.fancyTable.perPage*n.fancyTable.page?i(this).show():i(this).hide()):i(this).hide()}),n.fancyTable.pages=Math.ceil(n.fancyTable.matches/n.fancyTable.perPage),o.pagination){var a=n.fancyTable.paginationElement?i(n.fancyTable.paginationElement):i(n).find(".pag");a.empty();for(var e,t=1;t<=n.fancyTable.pages;t++)(1==t||t>n.fancyTable.page-(o.pagClosest+1)&&t<n.fancyTable.page+(o.pagClosest+1)||t==n.fancyTable.pages)&&(e=i("<a>",{html:t,"data-n":t,style:"margin:0.2em",class:o.paginationClass+" "+(t==n.fancyTable.page?o.paginationClassActive:"")}).css("cursor","pointer").bind("click",function(){n.fancyTable.page=i(this).data("n"),l.tableUpdate(n)}),t==n.fancyTable.pages&&n.fancyTable.page<n.fancyTable.pages-o.pagClosest-1&&a.append(i("<span>...</span>")),a.append(e),1==t&&n.fancyTable.page>o.pagClosest+2&&a.append(i("<span>...</span>")))}o.onUpdate.call(this,n)},this.isSearchMatch=function(a,e){if(o.matchCase||(a=a.toUpperCase(),e=e.toUpperCase()),"auto"==o.exactMatch&&e.match(/^".*?"$/))return a==(e=e.substring(1,e.length-1));if("auto"==o.exactMatch&&e.replace(/\s+/g,"").match(/^[<>]=?/)){var t=e.replace(/\s+/g,"").match(/^[<>]=?/)[0],n=e.replace(/\s+/g,"").substring(t.length);return">"==t&&+n<+a||"<"==t&&+a<+n||">="==t&&+n<=+a||"<="==t&&+a<=+n}if("auto"==o.exactMatch&&e.replace(/\s+/g,"").match(/^.+(\.\.|-).+$/)){n=e.replace(/\s+/g,"").split(/\.\.|-/);return+a>=+n[0]&&+a<=+n[1]}try{return!0===o.exactMatch?a==e:new RegExp(e).test(a)}catch{return!1}},this.reinit=function(){i(this).each(function(){i(this).find("th a").contents().unwrap(),i(this).find("tr.fancySearchRow").remove()}),i(this).fancyTable(this.settings)},this.tableSort=function(r){var a,e;void 0!==r.fancyTable.sortColumn&&r.fancyTable.sortColumn<r.fancyTable.nColumns&&(a=0,i(r).find("thead th").each(function(){i(this).attr("aria-sort",a==r.fancyTable.sortColumn?1==r.fancyTable.sortOrder?"ascending":-1==r.fancyTable.sortOrder?"descending":"other":null),a++}),i(r).find("thead th div.sortArrow").each(function(){i(this).remove()}),(e=i("<div>",{class:"sortArrow"}).css({margin:"0.1em",display:"inline-block",width:0,height:0,"border-left":"0.4em solid transparent","border-right":"0.4em solid transparent"})).css(0<r.fancyTable.sortOrder?{"border-top":"0.4em solid #000"}:{"border-bottom":"0.4em solid #000"}),i(r).find("thead th a").eq(r.fancyTable.sortColumn).append(e),e=i(r).find("tbody tr").toArray().sort(function(a,e){var t=i(a).find("td").eq(r.fancyTable.sortColumn),n=i(e).find("td").eq(r.fancyTable.sortColumn),t=i(t).attr("data-sortvalue")?i(t).data("sortvalue"):t.html(),n=i(n).attr("data-sortvalue")?i(n).data("sortvalue"):n.html();return"case-insensitive"==r.fancyTable.sortAs[r.fancyTable.sortColumn]&&(t=t.toLowerCase(),n=n.toLowerCase()),o.sortFunction.call(this,t,n,r.fancyTable,a,e)}),i(e).each(function(a){r.fancyTable.rowSortOrder[i(this).data("rowid")]=a}),i(r).find("tbody").empty().append(e))},this.each(function(){if("TABLE"!==i(this).prop("tagName"))return console.warn("fancyTable: Element is not a table."),!0;var e,t,a,n,r,s=this;s.fancyTable={nColumns:i(s).find("td").first().parent().find("td").length,nRows:i(this).find("tbody tr").length,perPage:o.perPage,page:1,pages:0,matches:0,searchArr:[],search:"",sortColumn:o.sortColumn,sortOrder:void 0!==o.sortOrder&&(new RegExp("desc","i").test(o.sortOrder)||-1==o.sortOrder)?-1:1,sortAs:[],paginationElement:o.paginationElement},s.fancyTable.rowSortOrder=new Array(s.fancyTable.nRows),0==i(s).find("tbody").length&&(n=i(s).html(),i(s).empty(),i(s).append("<tbody>").append(i(n))),0==i(s).find("thead").length&&i(s).prepend(i("<thead>")),i(s).find("tbody tr").each(function(a){i(this).data("rowid",a)}),o.sortable&&(e=0,i(s).find("thead th").each(function(){s.fancyTable.sortAs.push("numeric"==i(this).data("sortas")?"numeric":"case-insensitive"==i(this).data("sortas")?"case-insensitive":null);var a=i(this).html(),a=i("<a>",{href:"#","aria-label":"Sort by "+i(this).text(),html:a,"data-n":e,class:""}).css({cursor:"pointer",color:"inherit","text-decoration":"none","white-space":"nowrap"}).bind("click",function(){return s.fancyTable.sortColumn==i(this).data("n")?s.fancyTable.sortOrder=-s.fancyTable.sortOrder:s.fancyTable.sortOrder=1,s.fancyTable.sortColumn=i(this).data("n"),l.tableSort(s),l.tableUpdate(s),!1});i(this).empty(),i(this).append(a),e++})),o.searchable&&(t=i("<tr>").addClass("fancySearchRow"),o.globalSearch?(a=i("<input>",{"aria-label":"Search table",placeholder:o.inputPlaceholder,style:"width:100%;box-sizing:border-box;"+o.inputStyle}).bind("change paste keyup",function(){s.fancyTable.search=i(this).val(),s.fancyTable.page=1,l.tableUpdate(s)}),n=i("<th>",{style:"padding:2px;"}).attr("colspan",s.fancyTable.nColumns),i(a).appendTo(i(n)),i(n).appendTo(i(t))):(r=0,i(s).find("td").first().parent().find("td").each(function(){s.fancyTable.searchArr.push("");var a=i("<input>",{"aria-label":"Search column","data-n":r,placeholder:o.inputPlaceholder,style:"width:100%;box-sizing:border-box;"+o.inputStyle}).bind("change paste keyup",function(){s.fancyTable.searchArr[i(this).data("n")]=i(this).val(),s.fancyTable.page=1,l.tableUpdate(s)}),e=i("<th>",{style:"padding:2px;"});i(a).appendTo(i(e)),i(e).appendTo(i(t)),r++})),t.appendTo(i(s).find("thead"))),l.tableSort(s),o.pagination&&!o.paginationElement&&(i(s).find("tfoot").remove(),i(s).append(i("<tfoot><tr></tr></tfoot>")),i(s).find("tfoot tr").append(i("<td class='pag'></td>",{}).attr("colspan",s.fancyTable.nColumns))),l.tableUpdate(s),o.onInit.call(this,s)}),this}}(jQuery);
+ javascript/generated/popper.js@1.14.3 view
@@ -0,0 +1,5 @@+/*+ Copyright (C) Federico Zivolo 2018+ Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).+ */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:'top',o='top'===t?'scrollTop':'scrollLeft',n=e.nodeName;if('BODY'===n||'HTML'===n){var i=e.ownerDocument.documentElement,r=e.ownerDocument.scrollingElement||i;return r[o]}return e[o]}function f(e,t){var o=2<arguments.length&&void 0!==arguments[2]&&arguments[2],n=l(t,'top'),i=l(t,'left'),r=o?-1:1;return e.top+=n*r,e.bottom+=n*r,e.left+=i*r,e.right+=i*r,e}function m(e,t){var o='x'===t?'Left':'Top',n='Left'==o?'Right':'Bottom';return parseFloat(e['border'+o+'Width'],10)+parseFloat(e['border'+n+'Width'],10)}function h(e,t,o,n){return $(t['offset'+e],t['scroll'+e],o['client'+e],o['offset'+e],o['scroll'+e],r(10)?o['offset'+e]+n['margin'+('Height'===e?'Top':'Left')]+n['margin'+('Height'===e?'Bottom':'Right')]:0)}function c(){var e=document.body,t=document.documentElement,o=r(10)&&getComputedStyle(t);return{height:h('Height',e,t,o),width:h('Width',e,t,o)}}function g(e){return le({},e,{right:e.left+e.width,bottom:e.top+e.height})}function u(e){var o={};try{if(r(10)){o=e.getBoundingClientRect();var n=l(e,'top'),i=l(e,'left');o.top+=n,o.left+=i,o.bottom+=n,o.right+=i}else o=e.getBoundingClientRect()}catch(t){}var p={left:o.left,top:o.top,width:o.right-o.left,height:o.bottom-o.top},s='HTML'===e.nodeName?c():{},d=s.width||e.clientWidth||p.right-p.left,a=s.height||e.clientHeight||p.bottom-p.top,f=e.offsetWidth-d,h=e.offsetHeight-a;if(f||h){var u=t(e);f-=m(u,'x'),h-=m(u,'y'),p.width-=f,p.height-=h}return g(p)}function b(e,o){var i=2<arguments.length&&void 0!==arguments[2]&&arguments[2],p=r(10),s='HTML'===o.nodeName,d=u(e),a=u(o),l=n(e),m=t(o),h=parseFloat(m.borderTopWidth,10),c=parseFloat(m.borderLeftWidth,10);i&&'HTML'===o.nodeName&&(a.top=$(a.top,0),a.left=$(a.left,0));var b=g({top:d.top-a.top-h,left:d.left-a.left-c,width:d.width,height:d.height});if(b.marginTop=0,b.marginLeft=0,!p&&s){var y=parseFloat(m.marginTop,10),w=parseFloat(m.marginLeft,10);b.top-=h-y,b.bottom-=h-y,b.left-=c-w,b.right-=c-w,b.marginTop=y,b.marginLeft=w}return(p&&!i?o.contains(l):o===l&&'BODY'!==l.nodeName)&&(b=f(b,o)),b}function y(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=e.ownerDocument.documentElement,n=b(e,o),i=$(o.clientWidth,window.innerWidth||0),r=$(o.clientHeight,window.innerHeight||0),p=t?0:l(o),s=t?0:l(o,'left'),d={top:p-n.top+n.marginTop,left:s-n.left+n.marginLeft,width:i,height:r};return g(d)}function w(e){var n=e.nodeName;return'BODY'===n||'HTML'===n?!1:'fixed'===t(e,'position')||w(o(e))}function E(e){if(!e||!e.parentElement||r())return document.documentElement;for(var o=e.parentElement;o&&'none'===t(o,'transform');)o=o.parentElement;return o||document.documentElement}function v(e,t,i,r){var p=4<arguments.length&&void 0!==arguments[4]&&arguments[4],s={top:0,left:0},d=p?E(e):a(e,t);if('viewport'===r)s=y(d,p);else{var l;'scrollParent'===r?(l=n(o(t)),'BODY'===l.nodeName&&(l=e.ownerDocument.documentElement)):'window'===r?l=e.ownerDocument.documentElement:l=r;var f=b(l,d,p);if('HTML'===l.nodeName&&!w(d)){var m=c(),h=m.height,g=m.width;s.top+=f.top-f.marginTop,s.bottom=h+f.top,s.left+=f.left-f.marginLeft,s.right=g+f.left}else s=f}return s.left+=i,s.top+=i,s.right-=i,s.bottom-=i,s}function x(e){var t=e.width,o=e.height;return t*o}function O(e,t,o,n,i){var r=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf('auto'))return e;var p=v(o,n,r,i),s={top:{width:p.width,height:t.top-p.top},right:{width:p.right-t.right,height:p.height},bottom:{width:p.width,height:p.bottom-t.bottom},left:{width:t.left-p.left,height:p.height}},d=Object.keys(s).map(function(e){return le({key:e},s[e],{area:x(s[e])})}).sort(function(e,t){return t.area-e.area}),a=d.filter(function(e){var t=e.width,n=e.height;return t>=o.clientWidth&&n>=o.clientHeight}),l=0<a.length?a[0].key:d[0].key,f=e.split('-')[1];return l+(f?'-'+f:'')}function L(e,t,o){var n=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,i=n?E(t):a(t,o);return b(o,i,n)}function S(e){var t=getComputedStyle(e),o=parseFloat(t.marginTop)+parseFloat(t.marginBottom),n=parseFloat(t.marginLeft)+parseFloat(t.marginRight),i={width:e.offsetWidth+n,height:e.offsetHeight+o};return i}function T(e){var t={left:'right',right:'left',bottom:'top',top:'bottom'};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function C(e,t,o){o=o.split('-')[0];var n=S(e),i={width:n.width,height:n.height},r=-1!==['right','left'].indexOf(o),p=r?'top':'left',s=r?'left':'top',d=r?'height':'width',a=r?'width':'height';return i[p]=t[p]+t[d]/2-n[d]/2,i[s]=o===s?t[s]-n[a]:t[T(s)],i}function D(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function N(e,t,o){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===o});var n=D(e,function(e){return e[t]===o});return e.indexOf(n)}function P(t,o,n){var i=void 0===n?t:t.slice(0,N(t,'name',n));return i.forEach(function(t){t['function']&&console.warn('`modifier.function` is deprecated, use `modifier.fn`!');var n=t['function']||t.fn;t.enabled&&e(n)&&(o.offsets.popper=g(o.offsets.popper),o.offsets.reference=g(o.offsets.reference),o=n(o,t))}),o}function k(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=L(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=O(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=C(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?'fixed':'absolute',e=P(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function W(e,t){return e.some(function(e){var o=e.name,n=e.enabled;return n&&o===t})}function B(e){for(var t=[!1,'ms','Webkit','Moz','O'],o=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<t.length;n++){var i=t[n],r=i?''+i+o:e;if('undefined'!=typeof document.body.style[r])return r}return null}function H(){return this.state.isDestroyed=!0,W(this.modifiers,'applyStyle')&&(this.popper.removeAttribute('x-placement'),this.popper.style.position='',this.popper.style.top='',this.popper.style.left='',this.popper.style.right='',this.popper.style.bottom='',this.popper.style.willChange='',this.popper.style[B('transform')]=''),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function A(e){var t=e.ownerDocument;return t?t.defaultView:window}function M(e,t,o,i){var r='BODY'===e.nodeName,p=r?e.ownerDocument.defaultView:e;p.addEventListener(t,o,{passive:!0}),r||M(n(p.parentNode),t,o,i),i.push(p)}function I(e,t,o,i){o.updateBound=i,A(e).addEventListener('resize',o.updateBound,{passive:!0});var r=n(e);return M(r,'scroll',o.updateBound,o.scrollParents),o.scrollElement=r,o.eventsEnabled=!0,o}function F(){this.state.eventsEnabled||(this.state=I(this.reference,this.options,this.state,this.scheduleUpdate))}function R(e,t){return A(e).removeEventListener('resize',t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener('scroll',t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t}function U(){this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=R(this.reference,this.state))}function Y(e){return''!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function j(e,t){Object.keys(t).forEach(function(o){var n='';-1!==['width','height','top','right','bottom','left'].indexOf(o)&&Y(t[o])&&(n='px'),e.style[o]=t[o]+n})}function K(e,t){Object.keys(t).forEach(function(o){var n=t[o];!1===n?e.removeAttribute(o):e.setAttribute(o,t[o])})}function q(e,t,o){var n=D(e,function(e){var o=e.name;return o===t}),i=!!n&&e.some(function(e){return e.name===o&&e.enabled&&e.order<n.order});if(!i){var r='`'+t+'`';console.warn('`'+o+'`'+' modifier is required by '+r+' modifier in order to work, be sure to include it before '+r+'!')}return i}function G(e){return'end'===e?'start':'start'===e?'end':e}function z(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=me.indexOf(e),n=me.slice(o+1).concat(me.slice(0,o));return t?n.reverse():n}function V(e,t,o,n){var i=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+i[1],p=i[2];if(!r)return e;if(0===p.indexOf('%')){var s;switch(p){case'%p':s=o;break;case'%':case'%r':default:s=n;}var d=g(s);return d[t]/100*r}if('vh'===p||'vw'===p){var a;return a='vh'===p?$(document.documentElement.clientHeight,window.innerHeight||0):$(document.documentElement.clientWidth,window.innerWidth||0),a/100*r}return r}function _(e,t,o,n){var i=[0,0],r=-1!==['right','left'].indexOf(n),p=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=p.indexOf(D(p,function(e){return-1!==e.search(/,|\s/)}));p[s]&&-1===p[s].indexOf(',')&&console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');var d=/\s*,\s*|\s+/,a=-1===s?[p]:[p.slice(0,s).concat([p[s].split(d)[0]]),[p[s].split(d)[1]].concat(p.slice(s+1))];return a=a.map(function(e,n){var i=(1===n?!r:r)?'height':'width',p=!1;return e.reduce(function(e,t){return''===e[e.length-1]&&-1!==['+','-'].indexOf(t)?(e[e.length-1]=t,p=!0,e):p?(e[e.length-1]+=t,p=!1,e):e.concat(t)},[]).map(function(e){return V(e,i,t,o)})}),a.forEach(function(e,t){e.forEach(function(o,n){Y(o)&&(i[t]+=o*('-'===e[n-1]?-1:1))})}),i}function X(e,t){var o,n=t.offset,i=e.placement,r=e.offsets,p=r.popper,s=r.reference,d=i.split('-')[0];return o=Y(+n)?[+n,0]:_(n,p,s,d),'left'===d?(p.top+=o[0],p.left-=o[1]):'right'===d?(p.top+=o[0],p.left+=o[1]):'top'===d?(p.left+=o[0],p.top-=o[1]):'bottom'===d&&(p.left+=o[0],p.top+=o[1]),e.popper=p,e}for(var J=Math.min,Q=Math.round,Z=Math.floor,$=Math.max,ee='undefined'!=typeof window&&'undefined'!=typeof document,te=['Edge','Trident','Firefox'],oe=0,ne=0;ne<te.length;ne+=1)if(ee&&0<=navigator.userAgent.indexOf(te[ne])){oe=1;break}var i=ee&&window.Promise,ie=i?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},oe))}},re=ee&&!!(window.MSInputMethodContext&&document.documentMode),pe=ee&&/MSIE 10/.test(navigator.userAgent),se=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},de=function(){function e(e,t){for(var o,n=0;n<t.length;n++)o=t[n],o.enumerable=o.enumerable||!1,o.configurable=!0,'value'in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}return function(t,o,n){return o&&e(t.prototype,o),n&&e(t,n),t}}(),ae=function(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e},le=Object.assign||function(e){for(var t,o=1;o<arguments.length;o++)for(var n in t=arguments[o],t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},fe=['auto-start','auto','auto-end','top-start','top','top-end','right-start','right','right-end','bottom-end','bottom','bottom-start','left-end','left','left-start'],me=fe.slice(3),he={FLIP:'flip',CLOCKWISE:'clockwise',COUNTERCLOCKWISE:'counterclockwise'},ce=function(){function t(o,n){var i=this,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};se(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=ie(this.update.bind(this)),this.options=le({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=o&&o.jquery?o[0]:o,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(le({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){i.options.modifiers[e]=le({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return le({name:e},i.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(t){t.enabled&&e(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)}),this.update();var p=this.options.eventsEnabled;p&&this.enableEventListeners(),this.state.eventsEnabled=p}return de(t,[{key:'update',value:function(){return k.call(this)}},{key:'destroy',value:function(){return H.call(this)}},{key:'enableEventListeners',value:function(){return F.call(this)}},{key:'disableEventListeners',value:function(){return U.call(this)}}]),t}();return ce.Utils=('undefined'==typeof window?global:window).PopperUtils,ce.placements=fe,ce.Defaults={placement:'bottom',positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,o=t.split('-')[0],n=t.split('-')[1];if(n){var i=e.offsets,r=i.reference,p=i.popper,s=-1!==['bottom','top'].indexOf(o),d=s?'left':'top',a=s?'width':'height',l={start:ae({},d,r[d]),end:ae({},d,r[d]+r[a]-p[a])};e.offsets.popper=le({},p,l[n])}return e}},offset:{order:200,enabled:!0,fn:X,offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var o=t.boundariesElement||p(e.instance.popper);e.instance.reference===o&&(o=p(o));var n=B('transform'),i=e.instance.popper.style,r=i.top,s=i.left,d=i[n];i.top='',i.left='',i[n]='';var a=v(e.instance.popper,e.instance.reference,t.padding,o,e.positionFixed);i.top=r,i.left=s,i[n]=d,t.boundaries=a;var l=t.priority,f=e.offsets.popper,m={primary:function(e){var o=f[e];return f[e]<a[e]&&!t.escapeWithReference&&(o=$(f[e],a[e])),ae({},e,o)},secondary:function(e){var o='right'===e?'left':'top',n=f[o];return f[e]>a[e]&&!t.escapeWithReference&&(n=J(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]<r(n[d])&&(e.offsets.popper[d]=r(n[d])-o[a]),o[d]>r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-u<s[m]&&(e.offsets.popper[m]-=s[m]-(d[c]-u)),d[m]+u>s[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=$(J(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Q(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=z(n);break;case he.COUNTERCLOCKWISE:p=z(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)<f(l.right)||'top'===n&&f(a.bottom)>f(l.top)||'bottom'===n&&f(a.top)<f(l.bottom),h=f(a.left)<f(o.left),c=f(a.right)>f(o.right),g=f(a.top)<f(o.top),u=f(a.bottom)>f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=G(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottom<o.top||t.left>o.right||t.top>o.bottom||t.right<o.left){if(!0===e.hide)return e;e.hide=!0,e.attributes['x-out-of-boundaries']=''}else{if(!1===e.hide)return e;e.hide=!1,e.attributes['x-out-of-boundaries']=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var o=t.x,n=t.y,i=e.offsets.popper,r=D(e.instance.modifiers,function(e){return'applyStyle'===e.name}).gpuAcceleration;void 0!==r&&console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');var s,d,a=void 0===r?t.gpuAcceleration:r,l=p(e.instance.popper),f=u(l),m={position:i.position},h={left:Z(i.left),top:Q(i.top),bottom:Q(i.bottom),right:Z(i.right)},c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?-f.height+h.bottom:h.top,s='right'==g?-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var y='bottom'==c?-1:1,w='right'==g?-1:1;m[c]=d*y,m[g]=s*w,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return j(e.instance.popper,e.styles),K(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&j(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),j(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ce});+//# sourceMappingURL=popper.min.js.map
javascript/stylesheet.css view
@@ -9,9 +9,7 @@ background-color: transparent; color: black; }-.container { width: 90%;- ; max-width: 90%;- }+.container-fluid { padding: 1em 2em; } /* Reset milligram table styling so tooltips render correctly */ table, caption, tbody, tfoot, thead, tr, th, td {@@ -51,4 +49,16 @@ .cheader{ margin-left:10px;+}++h1 {+ font-size: 1em;+}++.not-available {+ color: grey;+}++.custom-tab {+ margin-top: 1em; }
javascript/tablogic.js view
@@ -1,26 +1,5 @@-function changeTab(tabName,elmnt) {- var i, tabviz, tablinks;-- // Hide all elements with class="tabviz" by default */- tabviz = document.getElementsByClassName("tabviz");- for (i = 0; i < tabviz.length; i++) {- tabviz[i].style.display = "none";- }-- // Remove the background color of all tablinks/buttons- tablinks = document.getElementsByClassName("tablink");- for (i = 0; i < tablinks.length; i++) {- tablinks[i].classList.remove('button-outline');- }-- // Show the specific tab content- document.getElementById(tabName).style.display = "block";-- // Add the specific color to the button used to open the tab content- elmnt.classList.add('button-outline');- $.sparkline_display_visible()--}--// Get the element with id="defaultOpen" and click on it-document.getElementById("defaultOpen").click();+$(document).ready(function () {+ $("#closures-tab").on("shown.bs.tab", function (e) {+ $.sparkline_display_visible();+ });+});
main/Main.hs view
@@ -17,7 +17,6 @@ import Eventlog.Data import Eventlog.Types import Paths_eventlog2html (version)-import Eventlog.Ticky main :: IO () main = do@@ -58,17 +57,13 @@ doOneJson :: Args -> FilePath -> FilePath -> IO () doOneJson a fin fout = do- HeapProfile (_, val, _, _) <- generateJson fin a+ Just (HeapProfileData val _ _) <- eventlogHeapProfile <$> generateJson fin a encodeFile fout val doOneHtml :: Args -> FilePath -> FilePath -> IO () doOneHtml a fin fout = do prof_type <- generateJsonValidate checkTraces fin a- let html = case prof_type of- HeapProfile (header, data_json, descs, closure_descs) ->- templateString header data_json descs closure_descs a- TickyProfile (header, tallocs, ticked_per, dat) ->- tickyTemplateString header tallocs ticked_per dat a+ let html = templateString prof_type a writeFile fout html where checkTraces :: ProfData -> IO ()
src/Eventlog/Args.hs view
@@ -21,7 +21,7 @@ = ShowVersion | Run Args -data Sort = Size | StdDev | Name | Gradient+data Sort = Size | StdDev | Name | Gradient | Number data Args = Args {@@ -47,7 +47,7 @@ argParser = Run <$> (Args <$> option parseSort ( long "sort"- <> help "How to sort the bands. One of: size (default), stddev, name, gradient."+ <> help "How to sort the bands. One of: size (default), stddev, name, number, gradient." <> value Size <> metavar "FIELD" ) <*> switch@@ -118,8 +118,9 @@ "size" -> Right Size "stddev" -> Right StdDev "name" -> Right Name+ "number" -> Right Number "gradient" -> Right Gradient- _ -> Left "expected one of: size, stddev, name"+ _ -> Left "expected one of: size, stddev, name, number" args :: IO Option args = execParser argsInfo
src/Eventlog/AssetVersions.hs view
@@ -39,3 +39,6 @@ datatablesButtonsHTML5Version,datatablesButtonsHTML5URL :: String datatablesButtonsHTML5Version = "2.0.1" datatablesButtonsHTML5URL = "https://cdn.datatables.net/buttons/2.0.1/js/buttons.html5.min.js"+popperVersion,popperURL :: String+popperVersion = "1.14.3"+popperURL = "https://cdn.jsdelivr.net/npm/popper.js@1.14.3"
src/Eventlog/Data.hs view
@@ -1,10 +1,18 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-}-module Eventlog.Data (generateJson, generateJsonValidate, generateJsonData, EventlogType(..) ) where+module Eventlog.Data+ ( generateJson+ , generateJsonValidate+ , generateJsonData+ , EventlogType(..)+ , HeapProfileData(..)+ , TickyProfileData(..)+ ) where import Prelude hiding (readFile)-import Data.Aeson (Value(..), (.=), object)+import Data.Aeson ((.=), object) import qualified Data.Map as Map+import Data.Maybe import Eventlog.Args (Args(..)) import Eventlog.Bands (bands)@@ -12,25 +20,19 @@ import qualified Eventlog.HeapProf as H import Eventlog.Prune import Eventlog.Vega-import Eventlog.Types (Header(..), ProfData(..), HeapProfBreakdown(..))+import Eventlog.Types import Data.List import Data.Ord import Eventlog.Trie import Eventlog.Detailed-import Text.Blaze.Html import Eventlog.Ticky-import Data.Word --data EventlogType = HeapProfile (Header, Value, Maybe Value, Maybe Html)- | TickyProfile (Header, Word64, Double, Html)--generateJsonData :: Args -> ProfData -> IO (Header, Value, Maybe Value, Maybe Html)-generateJsonData a (ProfData h binfo ccMap fs traces heap_info ipes _ticky_counter _ticky_samples _total_allocs) = do+generateJsonData :: Args -> ProfData -> HeapProfileData+generateJsonData a (ProfData h binfo ccMap fs traces heap_info ipes _ticky_counter _ticky_samples _total_allocs) = let keeps = pruneBands a binfo bs = bands h (Map.map fst keeps) fs combinedJson = object [- "samples" .= bandsToVega keeps bs+ "samples" .= bandsToVega bucket_desc keeps bs , "traces" .= tracesToVega traces , "heap" .= heapToVega heap_info ]@@ -41,16 +43,27 @@ Just HeapProfBreakdownCostCentre -> Just (outputTree ccMap mdescs) _ -> Nothing - let use_ipes = case hHeapProfileType h of+ use_ipes = case hHeapProfileType h of Just HeapProfBreakdownInfoTable -> Just ipes _ -> Nothing++ -- If we have IPE info, try to translate info table pointers to names+ bucket_desc bucket_info = fromMaybe desc $ do+ ipe_map <- use_ipes+ iptr <- toItblPointer_maybe (Bucket desc)+ itl <- Map.lookup iptr ipe_map+ pure (itlName itl <> " (" <> desc <> ")")+ where+ desc = shortDescription bucket_info+ desc_buckets = pruneDetailed a binfo bs' = bands h (Map.map fst desc_buckets) fs closure_table = case detailedLimit a of Just 0 -> Nothing+ _ | null desc_buckets -> Nothing _ -> Just (renderClosureInfo bs' use_ipes desc_buckets)- return (h, combinedJson, cc_descs, closure_table)+ in HeapProfileData combinedJson cc_descs closure_table generateJson :: FilePath -> Args -> IO EventlogType generateJson = generateJsonValidate (const (return ()))@@ -61,14 +74,12 @@ let chunk = if heapProfile a then H.chunk else E.chunk a dat <- chunk file validate dat- case profTickySamples dat of- [] -> HeapProfile <$> generateJsonData a dat- -- If there are any ticky samples then generate a ticky profile- _ -> do- let (percen, html) = renderTicky (profTotalAllocations dat) (profTickyCounters dat) (profItl dat) (profTickySamples dat)- return $ TickyProfile ( profHeader dat- , profTotalAllocations dat- , percen- , html )-+ pure $ EventlogType (profHeader dat)+ (Just (generateJsonData a dat))+ (if not (null (profTickySamples dat)) then Just (generateTickyData dat) else Nothing)+ -- If there are any ticky samples then generate a ticky profile +generateTickyData :: ProfData -> TickyProfileData+generateTickyData dat =+ let (percen, html) = renderTicky (profTotalAllocations dat) (profTickyCounters dat) (profItl dat) (profTickySamples dat)+ in TickyProfileData (profTotalAllocations dat) percen html
src/Eventlog/Detailed.hs view
@@ -29,7 +29,7 @@ Just ipes -> mkClosureInfo (\k _ -> toItblPointer k) raw_bs ipes Nothing -> Map.map (\v -> (None, v)) raw_bs - H.table ! A.id "closure_table" ! A.class_ "table table-striped closureTable" ! A.hidden "true" $ do+ H.table ! A.id "closure_table" ! A.class_ "table table-striped closureTable" $ do H.thead $ H.tr $ do H.th "Profile" numTh "n"@@ -114,7 +114,6 @@ \$.fn.sparkline.defaults.common.chartRangeMin = 0;\ \$.fn.sparkline.defaults.common.width = 200;\ \$('.linechart').sparkline();\- \$(\".closureTable\").removeAttr(\"hidden\")\ \});" getBandValues :: Int
src/Eventlog/HtmlTemplate.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} module Eventlog.HtmlTemplate where import Data.Aeson (Value, encode)@@ -6,38 +7,42 @@ import Data.String import Data.Text (Text, append) import qualified Data.Text as T-import qualified Data.Text.Lazy.Encoding as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Lazy.Encoding as TL import qualified Data.Text.Lazy as TL --import Text.Blaze.Html import Text.Blaze.Html5 as H import Text.Blaze.Html5.Attributes as A import Text.Blaze.Html.Renderer.String +import Data.FileEmbed+import Eventlog.Data import Eventlog.Javascript import Eventlog.Args import Eventlog.Types (Header(..), HeapProfBreakdown(..))+import Eventlog.Rendering.Bootstrap+import Eventlog.Rendering.Types import Eventlog.VegaTemplate import Eventlog.AssetVersions+import Eventlog.Ticky (tickyTab) import Paths_eventlog2html import Data.Version import Control.Monad import Data.Maybe -type VizID = Int- insertJsonData :: Value -> Html insertJsonData dat = preEscapedToHtml $ T.unlines [ "data_json= " `append` dat' `append` ";" , "console.log(data_json);" ] where- dat' = TL.toStrict (T.decodeUtf8 (encode dat))+ dat' = TL.toStrict (TL.decodeUtf8 (encode dat)) insertJsonDesc :: Value -> Html insertJsonDesc dat = preEscapedToHtml $ T.unlines [ "desc_json= " `append` dat' `append` ";" , "console.log(desc_json);" ] where- dat' = TL.toStrict (T.decodeUtf8 (encode dat))+ dat' = TL.toStrict (TL.decodeUtf8 (encode dat)) -- Dynamically bound in ccs tree insertColourScheme :: Text -> Html@@ -51,16 +56,14 @@ where line t = "res.view.insert(\"data_json_" <> t <>"\", data_json."<> t <>");" -data IncludeTraceData = TraceData | NoTraceData--encloseScript :: [Text] -> VizID -> Text -> Html+encloseScript :: [Text] -> TabID -> Text -> Html encloseScript = encloseScriptX -encloseRawVegaScript :: VizID -> Text -> Html+encloseRawVegaScript :: TabID -> Text -> Html encloseRawVegaScript = encloseScriptX [] -encloseScriptX :: [Text] -> VizID -> Text -> Html-encloseScriptX insert_data_sets vid vegaspec = preEscapedToHtml $ T.unlines ([+encloseScriptX :: [Text] -> TabID -> Text -> Html+encloseScriptX insert_data_sets (TabID vidt) vegaspec = preEscapedToHtml $ T.unlines ([ "var yourVlSpec" `append` vidt `append`"= " `append` vegaspec `append` ";" , "vegaEmbed('#vis" `append` vidt `append` "', yourVlSpec" `append` vidt `append` ")" , ".then((res) => { " ]@@ -71,24 +74,24 @@ [ "; res.view.resize()" , "; res.view.runAsync()" , "})" ])- where- vidt = T.pack $ show vid jsScript :: String -> Html jsScript url = script ! src (fromString $ url) $ "" css :: AttributeValue -> Html css url = link ! rel "stylesheet" ! href url -htmlHeader :: Value -> Maybe Value -> Args -> Html-htmlHeader dat desc as =+htmlHeader :: Maybe HeapProfileData -> Maybe TickyProfileData -> Args -> Html+htmlHeader mb_hpd mb_ticky as = H.head $ do H.title "eventlog2html - Heap Profile" meta ! charset "UTF-8"- script $ insertJsonData dat- maybe (return ()) (script . insertJsonDesc) desc+ forM_ mb_hpd $ \ (HeapProfileData dat desc _) -> do+ script $ insertJsonData dat+ maybe (return ()) (script . insertJsonDesc) desc script $ insertColourScheme (userColourScheme as) if not (noIncludejs as) then do+ script $ preEscapedToHtml popper script $ preEscapedToHtml vegaLite script $ preEscapedToHtml vega script $ preEscapedToHtml vegaEmbed@@ -97,81 +100,73 @@ script $ preEscapedToHtml bootstrap script $ preEscapedToHtml fancytable script $ preEscapedToHtml sparkline+ when has_ticky $ do+ H.style $ preEscapedToHtml datatablesCSS+ H.style $ preEscapedToHtml datatablesButtonsCSS+ script $ preEscapedToHtml datatables+ script $ preEscapedToHtml datatablesButtons+ script $ preEscapedToHtml datatablesHtml5+ H.style $ preEscapedToHtml imagesCSS else do+ jsScript popperURL jsScript vegaURL jsScript vegaLiteURL jsScript vegaEmbedURL jsScript jqueryURL css (preEscapedStringValue bootstrapCSSURL) jsScript bootstrapURL- css "//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic" jsScript fancyTableURL jsScript sparklinesURL+ when has_ticky $ do+ css (preEscapedStringValue datatablesCSSURL)+ css (preEscapedStringValue datatablesButtonsCSSURL)+ jsScript datatablesURL+ jsScript datatablesButtonsURL+ jsScript datatablesButtonsHTML5URL+ when has_ticky $+ script $ preEscapedToHtml datatablesEllipsis -- Include this last to overwrite some milligram styling H.style $ preEscapedToHtml stylesheet-+ where+ has_ticky = isJust mb_ticky -template :: Header -> Value -> Maybe Value -> Maybe Html -> Args -> Html-template header' dat cc_descs closure_descs as = docTypeHtml $ do+template :: EventlogType+ -> Args+ -> [TabGroup]+ -> Html+template (EventlogType header' x y) as tab_groups = docTypeHtml $ do H.stringComment $ "Generated with eventlog2html-" <> showVersion version- htmlHeader dat cc_descs as- body $ H.div ! class_ "container" $ do- H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- h1 $ a ! href "https://mpickering.github.io/eventlog2html" $ "eventlog2html"-- H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- "Options: "- code $ toHtml $ hJob header'-+ htmlHeader x y as+ body $ H.div ! class_ "container-fluid" $ do+ H.div ! class_ "row" $ navbar tab_groups H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- "Created at: "- code $ toHtml $ hDate header'+ H.div ! class_ "col tab-content custom-tab" $ do+ forM_ tab_groups $ \group -> do+ case group of+ SingleTab tab -> renderTab header' tab+ ManyTabs _ tabs -> mapM_ (renderTab header') tabs - forM_ (hHeapProfileType header') $ \prof_type -> do- H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- "Type of profile: "- code $ toHtml $ ppHeapProfileType prof_type+ script $ preEscapedToHtml tablogic - H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- "Sampling rate in seconds: "- code $ toHtml $ hSamplingRate header'+renderTab :: Header -> Tab -> Html+renderTab header' tab =+ H.div ! A.id (toValue (tabIDToTabID (tabId tab))) ! class_ ("tab-pane tabviz " <> status) $ H.div ! class_ "row" $ do+ forM_ (tabContent tab) $ \stuff -> H.div ! class_ "col" $ do+ stuff+ perTabFooter header'+ forM_ (tabDocs tab) $ \docs -> H.div ! class_ "col" $ docs+ where+ status = if tabActive tab then "show active" else "" - H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- button ! class_ "tablink button-black" ! onclick "changeTab('areachart', this)" ! A.id "defaultOpen" $ "Area Chart"- button ! class_ "tablink button-black" ! onclick "changeTab('normalizedchart', this)" $ "Normalized"- button ! class_ "tablink button-black" ! onclick "changeTab('streamgraph', this)" $ "Streamgraph"- button ! class_ "tablink button-black" ! onclick "changeTab('linechart', this)" $ "Linechart"- button ! class_ "tablink button-black" ! onclick "changeTab('heapchart', this)" $ "Heap"- when (isJust cc_descs) $ do- button ! class_ "tablink button-black" ! onclick "changeTab('cost-centres', this)" $ "Cost Centres"- when (isJust closure_descs) $ do- button ! class_ "tablink button-black" ! onclick "changeTab('closures', this)" $ "Detailed"+perTabFooter :: Header -> Html+perTabFooter header' = do H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- let itd = if (noTraces as) then NoTraceData else TraceData- mapM_ (\(vid, chartname, conf) ->- H.div ! A.id chartname ! class_ "tabviz" $ do- renderChart itd conf True vid- (TL.toStrict (encodeToLazyText (vegaJson (htmlConf as conf)))))- [(1, "areachart", AreaChart Stacked)- ,(2, "normalizedchart", AreaChart Normalized)- ,(3, "streamgraph", AreaChart StreamGraph)- ,(4, "linechart", LineChart)- ,(5, "heapchart", HeapChart) ]-- when (isJust cc_descs) $ do- H.div ! A.id "cost-centres" ! class_ "tabviz" $ do- renderChart itd LineChart False 6 treevega- forM_ closure_descs $ \v -> do- H.div ! A.id "closures" ! class_ "tabviz" $ do- v- script $ preEscapedToHtml tablogic+ H.div ! class_ "col" $ do+ toHtml $ maybe "No heap profile" ppHeapProfileType (hHeapProfileType header')+ ", created at "+ code $ toHtml $ hDate header'+ " by "+ code $ toHtml $ hJob header' select_data :: IncludeTraceData -> ChartType -> [Text]@@ -186,27 +181,39 @@ htmlConf :: Args -> ChartType -> ChartConfig-htmlConf as ct = ChartConfig 1200 1000 (not (noTraces as)) (userColourScheme as) "set1" ct (fromIntegral <$> (fixedYAxis as))+htmlConf as ct =+ ChartConfig+ { cwidth = 1200+ , cheight = 1000+ , traces = not (noTraces as)+ , colourScheme = userColourScheme as+ , lineColourScheme = "set1"+ , chartType = ct+ , fixedYAxisExtent = fromIntegral <$> fixedYAxis as+ } -renderChart :: IncludeTraceData -> ChartType -> Bool -> VizID -> Text -> Html+renderChart :: IncludeTraceData -> ChartType -> Bool -> TabID -> Text -> Html renderChart itd ct vega_lite vid vegaSpec = do let fields = select_data itd ct- H.div ! A.id (fromString $ "vis" ++ show vid) ! class_ "chart" $ ""+ H.div ! A.id (toValue (tabIDToVizID vid)) ! class_ "chart" $ "" script ! type_ "text/javascript" $ do if vega_lite then encloseScript fields vid vegaSpec else encloseRawVegaScript vid vegaSpec -renderChartWithJson :: IncludeTraceData -> ChartType -> Int -> Value -> Text -> Html+renderChartWithJson :: IncludeTraceData -> ChartType -> TabID -> Value -> Text -> Html renderChartWithJson itd ct k dat vegaSpec = do script $ insertJsonData dat renderChart itd ct True k vegaSpec -templateString :: Header -> Value -> Maybe Value -> Maybe Html -> Args -> String-templateString header' dat cc_descs closure_descs as =- renderHtml $ template header' dat cc_descs closure_descs as+templateString :: EventlogType+ -> Args+ -> String+templateString x as =+ renderHtml $ template x as $ allTabs x as + ppHeapProfileType :: HeapProfBreakdown -> Text ppHeapProfileType (HeapProfBreakdownCostCentre) = "Cost centre profiling (implied by -hc)" ppHeapProfileType (HeapProfBreakdownModule) = "Profiling by module (implied by -hm)"@@ -216,3 +223,95 @@ ppHeapProfileType (HeapProfBreakdownBiography) = "Biographical profiling (implied by -hb)" ppHeapProfileType (HeapProfBreakdownClosureType) = "Basic heap profile (implied by -hT)" ppHeapProfileType (HeapProfBreakdownInfoTable) = "Info table profile (implied by -hi)"+++allTabs :: EventlogType+ -> Args+ -> [TabGroup]+allTabs (EventlogType h x y) as =+ [SingleTab (metaTab h as)] +++ maybe [] (allHeapTabs h as) x +++ [tickyProfileTabs y]++metaTab :: Header -> Args -> Tab+metaTab header' _as =+ (mkTab "Meta" "meta" metadata Nothing) { tabActive = True }+ where+ metadata = do+ "Rendered by "+ a ! href "https://mpickering.github.io/eventlog2html" $ "eventlog2html " <> toHtml (showVersion version)+ when (has_heap_profile header') $+ H.div ! class_ "row" $ do+ H.div ! class_ "col" $ do+ "Sampling rate: "+ code $ toHtml $ hSamplingRate header'+ " seconds between heap samples"++has_heap_profile :: Header -> Bool+has_heap_profile h = isJust (hHeapProfileType h)++allHeapTabs :: Header -> Args -> HeapProfileData -> [TabGroup]+allHeapTabs header' as x =+ [ heapTab as+ , heapProfileTabs header' as x+ , costCentresTab as x+ , detailedTab x+ ]++heapTab :: Args -> TabGroup+heapTab as = SingleTab $ mkTab "Heap" tabid (mk as HeapChart tabid) (Just heapDocs)+ where+ tabid = "heapchart"++heapDocs :: Html+heapDocs = H.div $ preEscapedToHtml $ T.decodeUtf8 $(embedFile "inline-docs/heap.html")+++heapProfileTabs :: Header -> Args -> HeapProfileData -> TabGroup+heapProfileTabs header' as _+ | has_heap_profile header' = ManyTabs "Heap Profile" $+ [ mkTab "Area Chart" "areachart" (mk as (AreaChart Stacked) "areachart") noDocs+ , mkTab "Normalized" "normalizedchart" (mk as (AreaChart Normalized) "normalizedchart") noDocs+ , mkTab "Streamgraph" "streamgraph" (mk as (AreaChart StreamGraph) "streamgraph") noDocs+ , mkTab "Linechart" "linechart" (mk as LineChart "linechart") noDocs+ ]+ | otherwise = SingleTab noHeapProfileTab++noHeapProfileTab :: Tab+noHeapProfileTab = mkUnavailableTab "Heap Profile" "heap_profile" noHeapProfileDocs++noHeapProfileDocs :: Html+noHeapProfileDocs = H.div $ preEscapedToHtml $ T.decodeUtf8 $(embedFile "inline-docs/no-heap-profile.html")+++mk :: Args -> ChartType -> TabID -> Html+mk as conf vid = renderChart itd conf True vid+ (TL.toStrict (encodeToLazyText (vegaJson (htmlConf as conf))))+ where+ itd = if noTraces as then NoTraceData else TraceData++detailedTab :: HeapProfileData -> TabGroup+detailedTab (HeapProfileData _dat _cc_descs closure_descs) =+ SingleTab $ mkOptionalTab "Detailed" "closures" Prelude.id noDocs noDetailedDocs closure_descs++noDetailedDocs :: Html+noDetailedDocs = H.div $ preEscapedToHtml $ T.decodeUtf8 $(embedFile "inline-docs/no-detailed.html")+++costCentresTab :: Args -> HeapProfileData -> TabGroup+costCentresTab as (HeapProfileData _dat cc_descs _) =+ SingleTab $ mkOptionalTab "Cost Centres" "costcentres" (const stuff) noDocs noCostCentresDocs cc_descs+ where+ tabIx = "costcentres"+ itd = if noTraces as then NoTraceData else TraceData+ stuff = renderChart itd LineChart False tabIx treevega++noCostCentresDocs :: Html+noCostCentresDocs = H.div $ preEscapedToHtml $ T.decodeUtf8 $(embedFile "inline-docs/no-cost-centres.html")+++tickyProfileTabs :: Maybe TickyProfileData -> TabGroup+tickyProfileTabs = SingleTab . mkOptionalTab "Ticky" "ticky" tickyTab noDocs noTickyDocs++noTickyDocs :: Html+noTickyDocs = H.div $ preEscapedToHtml $ T.decodeUtf8 $(embedFile "inline-docs/no-ticky.html")
src/Eventlog/Javascript.hs view
@@ -21,6 +21,8 @@ , stylesheet , tablogic , treevega++ , popper ) where import Data.Text@@ -81,3 +83,6 @@ treevega :: Text treevega = decodeUtf8 $(embedFile "javascript/ccmap.vg")++popper :: Text+popper = decodeUtf8 $(embedFile $ "javascript/generated/popper.js@" <> popperVersion)
src/Eventlog/Prune.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TypeApplications #-} module Eventlog.Prune ( pruneBands, pruneDetailed ) where@@ -11,6 +12,9 @@ import Eventlog.Args (Args(..), Sort(..)) import Data.Maybe+import Data.Word (Word64)+import Text.Read (readMaybe)+import qualified Data.Text as T type Compare a = a -> a -> Ordering @@ -21,15 +25,20 @@ getComparison Args { sorting = StdDev, reversing = True } = cmpStdDevAscending getComparison Args { sorting = Name, reversing = True } = cmpNameDescending getComparison Args { sorting = Name, reversing = False } = cmpNameAscending+getComparison Args { sorting = Number, reversing = True } = cmpNumberDescending+getComparison Args { sorting = Number, reversing = False } = cmpNumberAscending getComparison Args { sorting = Gradient, reversing = True } = cmpGradientAscending getComparison Args { sorting = Gradient, reversing = False } = cmpGradientDescending cmpNameAscending, cmpNameDescending,+ cmpNumberAscending, cmpNumberDescending, cmpStdDevAscending, cmpStdDevDescending, cmpSizeAscending, cmpSizeDescending, cmpGradientAscending, cmpGradientDescending :: Compare (Bucket, BucketInfo) cmpNameAscending = comparing fst cmpNameDescending = flip cmpNameAscending+cmpNumberAscending (Bucket a, _) (Bucket b, _) = comparing (readMaybe @Word64 . T.unpack) a b <> compare a b+cmpNumberDescending = flip cmpNumberAscending cmpStdDevAscending = comparing (bucketStddev . snd) cmpStdDevDescending = flip cmpStdDevAscending cmpSizeAscending = comparing (bucketTotal . snd)
+ src/Eventlog/Rendering/Bootstrap.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE OverloadedStrings #-}+module Eventlog.Rendering.Bootstrap where++import Eventlog.Rendering.Types++import Control.Monad+import Data.String+import qualified Data.Text as T+import Text.Blaze.Html5 as H+import Text.Blaze.Html5.Attributes as A+import Text.Blaze.Internal (attribute)++dataToggle :: AttributeValue -> Attribute+dataToggle = attribute "data-toggle" " data-toggle=\""+{-# INLINE dataToggle #-}++dataTarget :: AttributeValue -> Attribute+dataTarget = attribute "data-target" " data-target=\""+{-# INLINE dataTarget #-}++ariaControls :: AttributeValue -> Attribute+ariaControls = attribute "aria-controls" " aria-controls=\""+{-# INLINE ariaControls #-}++ariaExpanded :: AttributeValue -> Attribute+ariaExpanded = attribute "aria-expanded" " aria-expanded=\""+{-# INLINE ariaExpanded #-}++ariaLabel :: AttributeValue -> Attribute+ariaLabel = attribute "aria-label" " aria-label=\""+{-# INLINE ariaLabel #-}++navbar :: [TabGroup] -> Html+navbar tab_groups = do+ H.ul ! A.id "vizTabs" ! class_ "nav nav-tabs" $ do+ forM_ tab_groups $ \group -> do+ case group of+ SingleTab tab ->+ H.li ! class_ "nav-item" $+ H.a ! A.id (toValue (tabIDToNavItemID (tabId tab)))+ ! class_ (tabClasses tab)+ ! href (toValue (tabIDToHref (tabId tab)))+ ! dataToggle "tab"+ ! dataTarget (toValue (tabIDToHref (tabId tab)))+ $ fromString (tabName tab)+ ManyTabs group_name tabs ->+ H.li ! class_ "nav-item dropdown" $ do+ H.a ! class_ "nav-link dropdown-toggle"+ ! href "#"+ ! dataToggle "dropdown"+ $ fromString group_name+ H.div ! class_ "dropdown-menu" $+ forM_ tabs $ \tab ->+ H.a ! A.id (toValue (tabIDToNavItemID (tabId tab)))+ ! class_ "dropdown-item"+ ! href (toValue (tabIDToHref (tabId tab)))+ ! dataToggle "tab"+ ! dataTarget (toValue (tabIDToHref (tabId tab)))+ $ fromString (tabName tab)++tabClasses :: Tab -> AttributeValue+tabClasses tab = toValue $ T.intercalate " " $+ "nav-link" :+ [ "active" | tabActive tab ] +++ [ "not-available" | tabDisabled tab ]
+ src/Eventlog/Rendering/Types.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE OverloadedStrings #-}++module Eventlog.Rendering.Types where++import Data.Char+import Data.String+import qualified Data.Text as T+import Text.Blaze.Html5++data IncludeTraceData+ = TraceData+ | NoTraceData++-- | Tab IDs must be usable as HTML and Javascript identifiers, so we allow a+-- limited selection of characters. This is enforced only at runtime by+-- 'mkTabID' and the 'IsString' instance, but that seems good enough for now.+newtype TabID = TabID T.Text++instance IsString TabID where+ fromString = mkTabID++mkTabID :: String -> TabID+mkTabID s+ | all valid s = TabID (T.pack s)+ | otherwise = error $ "mkTabID: invalid tab ID: " ++ s+ where+ valid c = isAscii c && (isAlpha c || isDigit c || c == '_')++tabIDToVizID :: TabID -> T.Text+tabIDToVizID (TabID t) = "vis" <> t++tabIDToNavItemID :: TabID -> T.Text+tabIDToNavItemID (TabID t) = t <> "-tab"++tabIDToTabID :: TabID -> T.Text+tabIDToTabID (TabID t) = t++tabIDToHref :: TabID -> T.Text+tabIDToHref (TabID t) = "#" <> t+++data TabGroup = ManyTabs String [Tab]+ | SingleTab Tab++data Tab = Tab { tabName :: String+ , tabId :: TabID+ , tabContent :: Maybe Html+ , tabDocs :: Maybe Html+ , tabActive :: Bool -- ^ Active by default?+ , tabDisabled :: Bool+ }++mkTab :: String -> TabID -> Html -> Maybe Html -> Tab+mkTab name id_ content docs = Tab name id_ (Just content) docs False False++mkUnavailableTab :: String -> TabID -> Html -> Tab+mkUnavailableTab name id_ docs = Tab name id_ Nothing (Just docs) False True++mkOptionalTab :: String -> TabID -> (a -> Html) -> Maybe Html -> Html -> Maybe a -> Tab+mkOptionalTab name id_ mk_content docs no_docs mb = case mb of+ Nothing -> mkUnavailableTab name id_ no_docs+ Just v -> mkTab name id_ (mk_content v) docs++noDocs :: Maybe Html+noDocs = Nothing
src/Eventlog/Ticky.hs view
@@ -3,13 +3,11 @@ -- Functions for rendering ticky sample information {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE QuasiQuotes #-}-module Eventlog.Ticky where+module Eventlog.Ticky (tickyTab, renderTicky) where -import Eventlog.Types import qualified Data.Map as Map import Data.Word -import Data.String import qualified Data.Text as T --import Text.Blaze.Html import qualified Text.Blaze.Html5 as H@@ -17,38 +15,20 @@ ( preEscapedToHtml, toHtml, dataAttribute,- preEscapedStringValue,- stringComment, Html, (!),- AttributeValue,- body,- button, code, div,- docTypeHtml,- h1,- head,- link,- meta, script,- style, table, td, th, thead,- title, tr ) import Text.Blaze.Html5.Attributes as A- ( charset, class_, hidden, href, id, onclick, rel, src)-import Text.Blaze (customAttribute)-import Text.Blaze.Html.Renderer.String+ ( class_, id ) -import Eventlog.Javascript-import Eventlog.Args-import Eventlog.AssetVersions-import Paths_eventlog2html-import Data.Version ( showVersion )+import Eventlog.Types import Text.RawString.QQ import Data.Fixed import Control.Monad@@ -86,67 +66,8 @@ (sortBy (comparing tickySampleTime) samples) -jsScript :: String -> Html-jsScript url = script ! src (fromString $ url) $ ""-css :: AttributeValue -> Html-css url = link ! rel "stylesheet" ! href url--htmlHeader :: Args -> Html-htmlHeader as =- H.head $ do- H.title "eventlog2html - Ticky Profile"- meta ! charset "utf-8"- if not (noIncludejs as)- then do- script $ preEscapedToHtml jquery- H.style $ preEscapedToHtml bootstrapCSS- script $ preEscapedToHtml bootstrap- H.style $ preEscapedToHtml datatablesCSS- H.style $ preEscapedToHtml datatablesButtonsCSS- script $ preEscapedToHtml datatables- script $ preEscapedToHtml datatablesButtons- script $ preEscapedToHtml datatablesHtml5- H.style $ preEscapedToHtml imagesCSS- script $ preEscapedToHtml sparkline- else do- jsScript vegaURL- jsScript vegaLiteURL- jsScript vegaEmbedURL- jsScript jqueryURL- css (preEscapedStringValue bootstrapCSSURL)- jsScript bootstrapURL- css "https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic"- jsScript fancyTableURL- css (preEscapedStringValue datatablesCSSURL)- css (preEscapedStringValue datatablesButtonsCSSURL)- jsScript datatablesURL- jsScript datatablesButtonsURL- jsScript datatablesButtonsHTML5URL- jsScript sparklinesURL- script $ preEscapedToHtml datatablesEllipsis- -- Include this last to overwrite other styling- H.style $ preEscapedToHtml stylesheet---template :: Header -> Word64 -> Double -> Html -> Args -> Html-template header' total ticked_percen v as = docTypeHtml $ do- H.stringComment $ "Generated with eventlog2html-" <> showVersion version- htmlHeader as- body $ H.div ! class_ "container" $ do- H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- h1 $ H.a ! href "https://mpickering.github.io/eventlog2html" $ "eventlog2html"-- H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- "Options: "- code $ toHtml $ hJob header'-- H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- "Created at: "- code $ toHtml $ hDate header'-+tickyTab :: TickyProfileData -> Html+tickyTab (TickyProfileData total ticked_percen v) = do H.div ! class_ "row" $ do H.div ! class_ "column" $ do "Total Allocations: "@@ -154,19 +75,9 @@ H.div ! class_ "column cheader" $ do "Allocations Ticked (%): " code $ toHtml $ toHtml (render $ trunc (ticked_percen * 100))- H.div ! class_ "row" $ do- H.div ! class_ "column" $ do- button ! class_ "tablink button-black" ! onclick "changeTab('table', this)" ! A.id "defaultOpen" $ "Table"- H.div ! class_ "row" $ do H.div ! A.id "table" ! class_ "tabviz" $ v- script $ preEscapedToHtml tablogic --tickyTemplateString :: Header -> Word64 -> Double -> Html -> Args -> String-tickyTemplateString header' tot_allocs ticked_per ticky_table as =- renderHtml $ template header' tot_allocs ticked_per ticky_table as- -- Table rendering trunc :: Double -> Fixed E2 trunc = realToFrac@@ -178,7 +89,7 @@ -> Map.Map TickyCounterId (InfoTableLocStatus, (TickyCounter, AccumStats, Double)) -> Html renderTickyInfo with_ipe ticky_samples = do- H.table ! A.id "closure_table" ! A.class_ "table table-striped closureTable" ! A.hidden "true" $ do+ H.table ! A.id "closure_table" ! A.class_ "table table-striped closureTable" $ do H.thead $ H.tr $ headFoot -- H.th "Profile" -- numTh "n"@@ -266,21 +177,6 @@ | cl_args == 0 = (2 + fvs) * 8 | otherwise = (1 + fvs) * 8 ---renderSpark :: Int -> [(Double, Word64, Word64)] -> Html-renderSpark size vs = H.span ! A.class_ "linechart"- ! customAttribute "data-allocd" (H.preEscapedTextValue $ T.intercalate "," (map renderLine vs))- ! customAttribute "data-entries" (H.preEscapedTextValue $ T.intercalate "," (map renderLineEntries vs))- ! customAttribute "sparkChartRangeMax" (H.toValue max_alloc_n)- $ mempty- where- rdouble = T.pack . showFixed True . realToFrac @Double @(Fixed E2)- renderLine (x,w, _) = rdouble x <> ":" <> T.pack (show (w `Prelude.div` fromIntegral size))- renderLineEntries (x,_, e) = rdouble x <> ":" <> T.pack (show e)-- max_alloc_n = last_allocd `Prelude.div` (fromIntegral size)- (_, last_allocd, _) = Prelude.head vs initTable :: Bool -> T.Text initTable ipe =
src/Eventlog/Types.hs view
@@ -13,6 +13,7 @@ import Numeric import qualified Data.Text as T import qualified Data.Map as Map+import Text.Blaze.Html data Header = Header@@ -103,12 +104,18 @@ show (InfoTablePtr p) = "0x" ++ showHex p "" toItblPointer :: Bucket -> InfoTablePtr-toItblPointer (Bucket t) =+toItblPointer = either error id . toItblPointer_either++toItblPointer_maybe :: Bucket -> Maybe InfoTablePtr+toItblPointer_maybe = either (const Nothing) Just . toItblPointer_either++toItblPointer_either :: Bucket -> Either String InfoTablePtr+toItblPointer_either (Bucket t) = let s = drop 2 (T.unpack t) w64 = case readHex s of- ((n, ""):_) -> n- _ -> error (show t)- in InfoTablePtr w64+ ((n, ""):_) -> pure (InfoTablePtr n)+ _ -> Left (show t)+ in w64 data InfoTableLocStatus = None -- None of the entries have InfoTableLoc | Missing -- This one is just missing@@ -124,3 +131,15 @@ -> Map.Map k (InfoTableLocStatus, a) mkClosureInfo f b ipes = Map.mapWithKey (\k v -> (mkMissing $ Map.lookup (f k v) ipes, v)) b+++data EventlogType =+ EventlogType+ { eventlogHeader :: Header+ , eventlogHeapProfile :: Maybe HeapProfileData+ , eventlogTickyProfile :: Maybe TickyProfileData+ }++data HeapProfileData = HeapProfileData Value (Maybe Value) (Maybe Html)++data TickyProfileData = TickyProfileData Word64 Double Html
src/Eventlog/Vega.hs view
@@ -19,14 +19,15 @@ data VegaEntry = VegaEntry { x :: Double, y :: Double, k :: Int, c :: Text } deriving (Show, ToJSON, Generic) -bandsToVega :: Map Bucket (Int, BucketInfo)+bandsToVega :: (BucketInfo -> Text)+ -> Map Bucket (Int, BucketInfo) -> (UArray Int Double, UArray (Int, Int) Double) -> [VegaEntry]-bandsToVega ks (ts, vs) =+bandsToVega bucket_name ks (ts, vs) = let (t1, tn) = bounds ts go (i, binfo) rs = go_1 ++ rs where- txt = shortDescription binfo+ txt = bucket_name binfo go_1 :: [VegaEntry] go_1 = flip map [t1 .. tn] $ \t -> VegaEntry (ts ! t) (vs ! (i, t)) i txt
src/Eventlog/VegaTemplate.hs view
@@ -199,7 +199,7 @@ encodingHeapSelection c = encoding . tooltip []- . color [MName "c", MmType Nominal, MScale [lineColourProperty c], MLegend [LValues (LStrings ["Heap Size", "Blocks Size", "Live Bytes"])]]+ . color [MName "c", MmType Nominal, MScale [lineColourProperty c, SDomain (DStrings ["Heap Size", "Blocks Size", "Live Bytes"]) ]] . position X [PName "x", PmType Quantitative, PAxis [AxTitle "Time (s)"]] . position Y [PName "y", PmType Quantitative, PAxis [], PSort [ByFieldOp "k" Max]] @@ -260,10 +260,7 @@ . position Y ([PName "y" , PmType Quantitative , PAxis $ case ct of- Stacked -> [AxTitle "Allocation"- , AxFormat "s"- , AxTitlePadding 15.0- , AxMaxExtent 15.0]+ Stacked -> [AxTitle "Allocation", AxFormat "s" ] Normalized -> [AxTitle "Allocation (Normalized)", AxFormat "p"] StreamGraph -> [AxTitle "Allocation (Streamgraph)", AxLabels False, AxTicks False, AxTitlePadding 10.0] , PAggregate Sum