diff options
Diffstat (limited to 'lib')
93 files changed, 732 insertions, 8230 deletions
diff --git a/lib/.gitignore b/lib/.gitignore index b1da61706d..283393faa9 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -526,14 +526,6 @@ /orber/src/oe_erlang.erl /orber/src/oe_erlang.hrl -# percept - -/percept/doc/src/egd.xml -/percept/doc/src/egd_ug.xml -/percept/doc/src/percept.xml -/percept/doc/src/percept_profile.xml -/percept/doc/src/percept_ug.xml - # snmp snmp/doc/intex.html diff --git a/lib/Makefile b/lib/Makefile index a7f3c9192f..4740e6eb59 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -35,7 +35,7 @@ ALL_ERLANG_APPLICATIONS = xmerl edoc erl_docgen snmp otp_mibs erl_interface \ public_key ssl observer odbc diameter \ cosTransactions cosEvent cosTime cosNotification \ cosProperty cosFileTransfer cosEventDomain et megaco \ - eunit ssh typer percept eldap dialyzer hipe + eunit ssh typer eldap dialyzer hipe ifdef BUILD_ALL ERLANG_APPLICATIONS += $(ALL_ERLANG_APPLICATIONS) diff --git a/lib/percept/AUTHORS b/lib/percept/AUTHORS deleted file mode 100644 index f6c040ae76..0000000000 --- a/lib/percept/AUTHORS +++ /dev/null @@ -1,4 +0,0 @@ -Original Authors and Contributors: - -Bj�rn-Egil Dahlberg -Magnus Tho�ng diff --git a/lib/percept/Makefile b/lib/percept/Makefile deleted file mode 100644 index 1f51bd2fef..0000000000 --- a/lib/percept/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2007-2016. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# %CopyrightEnd% -# -include $(ERL_TOP)/make/target.mk -include $(ERL_TOP)/make/$(TARGET)/otp.mk - - -# ---------------------------------------------------- -# Common Macros -# ---------------------------------------------------- - -SUB_DIRECTORIES = src priv doc/src - -SPECIAL_TARGETS = - -# ---------------------------------------------------- -# Default Subdir Targets -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_subdir.mk diff --git a/lib/percept/c_src/.gitignore b/lib/percept/c_src/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/c_src/.gitignore +++ /dev/null diff --git a/lib/percept/doc/html/.gitignore b/lib/percept/doc/html/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/doc/html/.gitignore +++ /dev/null diff --git a/lib/percept/doc/man3/.gitignore b/lib/percept/doc/man3/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/doc/man3/.gitignore +++ /dev/null diff --git a/lib/percept/doc/pdf/.gitignore b/lib/percept/doc/pdf/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/doc/pdf/.gitignore +++ /dev/null diff --git a/lib/percept/doc/src/Makefile b/lib/percept/doc/src/Makefile deleted file mode 100644 index 2f84d61cbc..0000000000 --- a/lib/percept/doc/src/Makefile +++ /dev/null @@ -1,190 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2007-2016. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# %CopyrightEnd% -# - -include $(ERL_TOP)/make/target.mk -include $(ERL_TOP)/make/$(TARGET)/otp.mk - -# ---------------------------------------------------- -# Application version -# ---------------------------------------------------- -include ../../vsn.mk -VSN=$(PERCEPT_VSN) -APPLICATION=percept - -# ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) - -# ---------------------------------------------------- -# Help application directory specification -# ---------------------------------------------------- - -EDOC_DIR = $(ERL_TOP)/lib/edoc - -# ---------------------------------------------------- -# Target Specs -# ---------------------------------------------------- -PERCEPT_DIR = $(ERL_TOP)/lib/$(APPLICATION)/src -RUNTIME_TOOLS_DIR = $(ERL_TOP)/lib/runtime_tools/src - -PERCEPT_MODULES = \ - egd\ - percept - -RUNTIME_TOOLS_MODULES = \ - percept_profile - -XML_APPLICATION_FILES = \ - ref_man.xml - -PERCEPT_XML_FILES = $(PERCEPT_MODULES:=.xml) - -RUNTIME_TOOLS_XML_FILES = $(RUNTIME_TOOLS_MODULES:=.xml) - -MODULE_XML_FILES = $(PERCEPT_XML_FILES) $(RUNTIME_TOOLS_XML_FILES) - -XML_REF_MAN = \ - ref_man.xml - -XML_REF3_FILES = $(MODULE_XML_FILES) - -XML_PART_FILES = \ - part.xml \ - part_notes.xml - -XML_REF6_FILES = - -XML_CHAPTER_FILES = \ - notes.xml \ - egd_ug.xml \ - percept_ug.xml - -GEN_XML = \ - egd_ug.xml \ - percept_ug.xml - -BOOK_FILES = book.xml - -XML_FILES = \ - $(BOOK_FILES) $(XML_CHAPTER_FILES) \ - $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF_MAN) - -HTML_EXAMPLE_FILES = \ - percept_examples.html - -HTML_STYLESHEET_FILES = \ - ../stylesheet.css - - -GIF_FILES = \ - test1.gif \ - test2.gif \ - test3.gif \ - test4.gif \ - percept_overview.gif \ - percept_processes.gif \ - percept_processinfo.gif \ - percept_compare.gif \ - img_esi_result.gif - -# ---------------------------------------------------- -INFO_FILE = ../../info - -HTML_FILES = \ - $(XML_REF_MAN:%.xml=$(HTMLDIR)/%.html) \ - $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) - -MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) -MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6) - - -HTML_REF_MAN_FILE = $(HTMLDIR)/index.html - -TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf - - -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -XML_FLAGS += - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- -$(HTMLDIR)/%.gif: %.gif - $(INSTALL_DATA) $< $@ - -docs: pdf html man - -$(TOP_PDF_FILE): $(XML_FILES) - -pdf: $(TOP_PDF_FILE) - -html: gifs $(HTML_REF_MAN_FILE) - -clean clean_docs: - rm -f $(MODULE_XML_FILES) $(GEN_XML) - rm -rf $(HTMLDIR)/* - rm -f $(MAN3DIR)/* - rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) - rm -f errs core *~ - -man: $(MAN3_FILES) $(MAN6_FILES) - -gifs: $(GIF_FILES:%=$(HTMLDIR)/%) - -xml: $(MODULE_XML_FILES) - -$(PERCEPT_XML_FILES): - escript $(DOCGEN)/priv/bin/xml_from_edoc.escript $(PERCEPT_DIR)/$(@:%.xml=%.erl) - -$(RUNTIME_TOOLS_XML_FILES): - escript $(DOCGEN)/priv/bin/xml_from_edoc.escript $(RUNTIME_TOOLS_DIR)/$(@:%.xml=%.erl) - -info: - @echo "XML_PART_FILES: $(XML_PART_FILES)" - @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)" - @echo "PERCEPT_XML_FILES: $(MODULE_XML_FILES)" - @echo "PERCEPT_MODULES: $(PERCEPT_MODULES)" - @echo "HTML_FILES: $(HTML_FILES)" - @echo "HTMLDIR: $(HTMLDIR)" - - -debug opt: - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_docs_spec: docs - $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" - $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" - $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" - $(INSTALL_DATA) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) \ - $(HTMLDIR)/* \ - "$(RELSYSDIR)/doc/html" - $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" - $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" - $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" - -release_spec: - diff --git a/lib/percept/doc/src/book.xml b/lib/percept/doc/src/book.xml deleted file mode 100644 index 5acba1f214..0000000000 --- a/lib/percept/doc/src/book.xml +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE book SYSTEM "book.dtd"> - -<book xmlns:xi="http://www.w3.org/2001/XInclude"> - <header titlestyle="normal"> - <copyright> - <year>2007</year> - <year>2016</year> - <holder>Ericsson AB, All Rights Reserved</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - </legalnotice> - - <title>Percept</title> - <prepared>Björn-Egil Dahlberg</prepared> - <docno></docno> - <date>2007-11-02</date> - <rev>0.5.0</rev> - <file>book.xml</file> - </header> - <insidecover> - </insidecover> - <pagetext>Percept</pagetext> - <preamble> - <contents level="2"></contents> - </preamble> - <parts lift="no"> - <xi:include href="part.xml"/> - </parts> - <applications> - <xi:include href="ref_man.xml"/> - </applications> - <releasenotes> - <xi:include href="notes.xml"/> - </releasenotes> - <listofterms></listofterms> - <index></index> -</book> - diff --git a/lib/percept/doc/src/egd_ug.xmlsrc b/lib/percept/doc/src/egd_ug.xmlsrc deleted file mode 100644 index 85d41ada79..0000000000 --- a/lib/percept/doc/src/egd_ug.xmlsrc +++ /dev/null @@ -1,90 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2007</year> - <year>2016</year> - <holder>Ericsson AB, All Rights Reserved</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - </legalnotice> - - <title>egd</title> - <prepared>Björn-Egil Dahlberg</prepared> - <docno></docno> - <date>2007-11-03</date> - <rev>A</rev> - <file>egd_ug.xml</file> - </header> - <section> - <title>Introduction</title> - <p> - The egd module is an interface for 2d-image rendering and is used by - Percept to generate dynamic graphs to its web pages. All code is pure - erlang, no drivers needed. - </p> - <p> - The library is intended for small to medium image sizes with low - complexity for optimal performance. The library handles horizontal - lines better then vertical lines. - </p> - <p> - The foremost purpose for this module is to enable users to - generate images from erlang code and/or datasets and to - send these images to either files or web servers. - </p> - </section> - <section> - <title>File example</title> - <p>Drawing examples:</p> - <codeinclude file="img.erl" tag="" type="none"></codeinclude> - <p> First save. </p> - <image file="test1.gif"> - <icaption>test1.png</icaption> - </image> - - <p> Second save. </p> - <image file="test2.gif"> - <icaption>test2.png</icaption> - </image> - - <p> Third save. </p> - <image file="test3.gif"> - <icaption>test3.png</icaption> - </image> - - <p> Fourth save. </p> - <image file="test4.gif"> - <icaption>test4.png</icaption> - </image> - </section> - <section> - <title>ESI example</title> - <p>Using egd with inets ESI to generate images on the fly:</p> - <codeinclude file="img_esi.erl" tag="" type="none"></codeinclude> - <image file="img_esi_result.gif"> - <icaption>Example of result.</icaption> - </image> - <p> - For more information regarding ESI, please see inets application - <seealso marker="inets:mod_esi">mod_esi</seealso>. - </p> - </section> -</chapter> - - diff --git a/lib/percept/doc/src/fascicules.xml b/lib/percept/doc/src/fascicules.xml deleted file mode 100644 index 37feca543f..0000000000 --- a/lib/percept/doc/src/fascicules.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE fascicules SYSTEM "fascicules.dtd"> - -<fascicules> - <fascicule file="part" href="part_frame.html" entry="no"> - User's Guide - </fascicule> - <fascicule file="ref_man" href="ref_man_frame.html" entry="yes"> - Reference Manual - </fascicule> - <fascicule file="part_notes" href="part_notes_frame.html" entry="no"> - Release Notes - </fascicule> - <fascicule file="" href="../../../../doc/print.html" entry="no"> - Off-Print - </fascicule> -</fascicules> - diff --git a/lib/percept/doc/src/img.erl b/lib/percept/doc/src/img.erl deleted file mode 100644 index 8f3bd3839f..0000000000 --- a/lib/percept/doc/src/img.erl +++ /dev/null @@ -1,50 +0,0 @@ --module(img). - --export([do/0]). - -do() -> - Im = egd:create(200,200), - Red = egd:color({255,0,0}), - Green = egd:color({0,255,0}), - Blue = egd:color({0,0,255}), - Black = egd:color({0,0,0}), - Yellow = egd:color({255,255,0}), - - % Line and fillRectangle - - egd:filledRectangle(Im, {20,20}, {180,180}, Red), - egd:line(Im, {0,0}, {200,200}, Black), - - egd:save(egd:render(Im, png), "/home/egil/test1.png"), - - egd:filledEllipse(Im, {45, 60}, {55, 70}, Yellow), - egd:filledEllipse(Im, {145, 60}, {155, 70}, Blue), - - egd:save(egd:render(Im, png), "/home/egil/test2.png"), - - R = 80, - X0 = 99, - Y0 = 99, - - Pts = [ { X0 + trunc(R*math:cos(A*math:pi()*2/360)), - Y0 + trunc(R*math:sin(A*math:pi()*2/360)) - } || A <- lists:seq(0,359,5)], - lists:map( - fun({X,Y}) -> - egd:rectangle(Im, {X-5, Y-5}, {X+5,Y+5}, Green) - end, Pts), - - egd:save(egd:render(Im, png), "/home/egil/test3.png"), - - % Text - Filename = filename:join([code:priv_dir(percept), "fonts", "6x11_latin1.wingsfont"]), - Font = egd_font:load(Filename), - {W,H} = egd_font:size(Font), - String = "egd says hello", - Length = length(String), - - egd:text(Im, {round(100 - W*Length/2), 200 - H - 5}, Font, String, Black), - - egd:save(egd:render(Im, png), "/home/egil/test4.png"), - - egd:destroy(Im). diff --git a/lib/percept/doc/src/img_esi.erl b/lib/percept/doc/src/img_esi.erl deleted file mode 100644 index e9796819c0..0000000000 --- a/lib/percept/doc/src/img_esi.erl +++ /dev/null @@ -1,25 +0,0 @@ --module(img_esi). - --export([image/3]). - -image(SessionID, _Env, _Input) -> - mod_esi:deliver(SessionID, header()), - Binary = my_image(), - mod_esi:deliver(SessionID, binary_to_list(Binary)). - -my_image() -> - Im = egd:create(300,20), - Black = egd:color({0,0,0}), - Red = egd:color({255,0,0}), - egd:filledRectangle(Im, {30,14}, {270,19}, Red), - egd:rectangle(Im, {30,14}, {270,19}, Black), - - Filename = filename:join([code:priv_dir(percept), "fonts", "6x11_latin1.wingsfont"]), - Font = egd_font:load(Filename), - egd:text(Im, {30, 0}, Font, "egd with esi callback", Black), - Bin = egd:render(Im, png), - egd:destroy(Im), - Bin. - -header() -> - "Content-Type: image/png\r\n\r\n". diff --git a/lib/percept/doc/src/img_esi_result.gif b/lib/percept/doc/src/img_esi_result.gif Binary files differdeleted file mode 100644 index 6973392998..0000000000 --- a/lib/percept/doc/src/img_esi_result.gif +++ /dev/null diff --git a/lib/percept/doc/src/ipc_tree.erl b/lib/percept/doc/src/ipc_tree.erl deleted file mode 100644 index 89360379c6..0000000000 --- a/lib/percept/doc/src/ipc_tree.erl +++ /dev/null @@ -1,30 +0,0 @@ --module(ipc_tree). --export([go/1, init/2]). - -go(N) -> - start(N, self()), - receive {_,stop} -> ok end. - -start(Depth, ParentPid) -> - spawn(?MODULE, init, [Depth, ParentPid]). - -init(0, ParentPid) -> - workload(5000), - ParentPid ! {self(),stop}, - ok; -init(Depth, ParentPid) -> - Pid1 = spawn(?MODULE, init, [Depth - 1, self()]), - Pid2 = spawn(?MODULE, init, [Depth - 1, self()]), - main([Pid1,Pid2], ParentPid). - -main(Pids, ParentPid) -> - workload(5000), - gather(Pids), - ParentPid ! {self(),stop}, - ok. - -gather([]) -> ok; -gather([Pid|Pids]) -> receive {Pid,stop} -> gather(Pids) end. - -workload(0) -> ok; -workload(N) -> math:sin(2), workload(N - 1). diff --git a/lib/percept/doc/src/notes.xml b/lib/percept/doc/src/notes.xml deleted file mode 100644 index c9d5d3ae29..0000000000 --- a/lib/percept/doc/src/notes.xml +++ /dev/null @@ -1,495 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2007</year> - <year>2016</year> - <holder>Ericsson AB, All Rights Reserved</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - </legalnotice> - - <title>Percept Release Notes</title> - <prepared>otp_appnotes</prepared> - <docno>nil</docno> - <date>nil</date> - <rev>nil</rev> - <file>notes.xml</file> - </header> - <p>This document describes the changes made to the Percept application.</p> - -<section><title>Percept 0.9</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Remove deprecated <c>erlang:now/0</c> calls</p> - <p> - Own Id: OTP-13422</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Improve line implementation</p> - <p> - Add capabilities for line thickness and anti-aliasing.</p> - <p> - Own Id: OTP-13598</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.11</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fix http server configuration</p> - <p> - Own Id: OTP-12662</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.10</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Make sure to install .hrl files when needed</p> - <p> - Own Id: OTP-12197</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.9</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Application upgrade (appup) files are corrected for the - following applications: </p> - <p> - <c>asn1, common_test, compiler, crypto, debugger, - dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe, - inets, observer, odbc, os_mon, otp_mibs, parsetools, - percept, public_key, reltool, runtime_tools, ssh, - syntax_tools, test_server, tools, typer, webtool, wx, - xmerl</c></p> - <p> - A new test utility for testing appup files is added to - test_server. This is now used by most applications in - OTP.</p> - <p> - (Thanks to Tobias Schlager)</p> - <p> - Own Id: OTP-11744</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.8.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The encoding of the <c>notes.xml</c> file has been - changed from latin1 to utf-8 to avoid future merge - problems.</p> - <p> - Own Id: OTP-11310</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.8.1</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> Postscript files no longer needed for the generation - of PDF files have been removed. </p> - <p> - Own Id: OTP-11016</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.8</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Misc build updates</p> - <p> - Own Id: OTP-10784</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.7</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Add missing modules in app-file</p> - <p> - Own Id: OTP-10439</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.6.1</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Miscellaneous documentation build updates</p> - <p> - Own Id: OTP-9813</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.6</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fix message handling in select requests</p> - <p> - percept_db used to send results in untagged messages, and - use a non selective receive to extract them. When percept - is used from the shell process, this can confuse other - messages with the actual result.</p> - <p> - Add a tag to the message to be {result, Result}. Add - demonitor to avoid keeping DOWN message in the queue fix - one spec in do_start/0</p> - <p> - (Thanks to Ahmed Omar)</p> - <p> - Own Id: OTP-9490</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.5</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> Fixes a race condition found in percept_db start/1 - function. (Thanks to Ahmed Omar) </p> - <p> - Own Id: OTP-9012</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.4</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fix egd_render transparent to use float constants.</p> - <p> - The render engine has float guards to enhance beam code - generation. However, the default case used integers which - caused the engine to crash. This is now fixed.</p> - <p> - Own Id: OTP-8425</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p>The documentation is now possible to build in an open - source environment after a number of bugs are fixed and - some features are added in the documentation build - process. </p> - <p>- The arity calculation is updated.</p> - <p>- The module prefix used in the function names for - bif's are removed in the generated links so the links - will look like - "http://www.erlang.org/doc/man/erlang.html#append_element-2" - instead of - "http://www.erlang.org/doc/man/erlang.html#erlang:append_element-2".</p> - <p>- Enhanced the menu positioning in the html - documentation when a new page is loaded.</p> - <p>- A number of corrections in the generation of man - pages (thanks to Sergei Golovan)</p> - <p>- The legal notice is taken from the xml book file so - OTP's build process can be used for non OTP - applications.</p> - <p> - Own Id: OTP-8343</p> - </item> - <item> - <p> - Cleanups suggested by tidier and modernization of types - and specs.</p> - <p> - Own Id: OTP-8455</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.3</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The documentation is now built with open source tools - (xsltproc and fop) that exists on most platforms. One - visible change is that the frames are removed.</p> - <p> - Own Id: OTP-8201</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Extensions to <c>egd:color/1</c> for using atoms as color - definition in addition to rgb triplets.</p> - <p> - Own Id: OTP-7975</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.8.1</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p><c>egd</c> now supports encapsulated postscript output - format.</p> - <p> - Own Id: OTP-7923</p> - </item> - </list> - </section> - -</section> - - <section><title>Percept 0.8</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p>A problem with options list to percept causing some - options to be disregarded unintentionally. This has now - been fixed.</p> <p>An error in <c>percept_analyzer</c> - caused calculation of standard deviation to be incorrect. - This has now been corrected.</p> - <p> - Own Id: OTP-7693</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p>Updated css for percept server for enhanced - viewing.</p> <p>Increased performance of egd render.</p> - <p>Several graph errors could occur when compacting data - to decrease graph rendering time causing incorrect - scalability numbers. These errors have now been - fixed.</p> <p>Increased viewing width for graphs. The - viewing width is now dependent on client screen - resolution.</p> - <p> - Own Id: OTP-7696</p> - </item> - </list> - </section> - -</section> -<section><title>Percept 0.7.3</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p>External pids caused the webserver to crash. This has - now been fixed.</p> - <p> - Own Id: OTP-7515 Aux Id: seq11004 </p> - </item> - <item> - <p>Fixed a timestamp problem where some events could be - sent out of order. Minor fixes to presentation of - data.</p> - <p> - Own Id: OTP-7544 Aux Id: otp-7442 </p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p>Performance enhancement for the egd render engine - (Thanks to Magnus Thoäng).</p> - <p> - Own Id: OTP-7616</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.7.2</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p>Calling <c>egd:destroy/1</c> did not properly remove - the process holding the image.</p> - <p>Synchronous calls done via the egd interface could - erroneous receive messages not intended for egd. Messages - are now tagged in such a way so this should not - occur.</p> - <p> - Own Id: OTP-7336</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.7.1</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fixed out of bounds rendering problem in egd which could - cause the rendering process to crash.</p> - <p> - Own Id: OTP-7215</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.7</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p>Percept no longer depends on external c-libraries. The - graphical rendering is now done via erlang code.</p> - <p> - Own Id: OTP-7162</p> - </item> - </list> - </section> - -</section> - -<section><title>Percept 0.6.2</title> - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - A new module, percept_profile, can now be used to collect - profiling data even if the percept application is not - installed. This should help profiling erlang application - on target machines without libgd installed.</p> - <p> - Own Id: OTP-7126</p> - </item> - </list> - </section> - -</section> - -<section> - <title>Percept 0.5.0</title> - <section><title>First Release</title> - <list> - <item> - <p> - First Release. - </p> - <p>Own Id: OTP-6783</p> - </item> - </list> - </section> - </section> -</chapter> - diff --git a/lib/percept/doc/src/part.xml b/lib/percept/doc/src/part.xml deleted file mode 100644 index 277d89d45c..0000000000 --- a/lib/percept/doc/src/part.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE part SYSTEM "part.dtd"> - -<part xmlns:xi="http://www.w3.org/2001/XInclude"> - <header> - <copyright> - <year>2007</year> - <year>2016</year> - <holder>Ericsson AB, All Rights Reserved</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - </legalnotice> - - <title>Percept User's Guide</title> - <prepared>Björn-Egil Dahlberg</prepared> - <docno></docno> - <date>2007-11-02</date> - <rev>0.5.0</rev> - <file>part.xml</file> - </header> - <description> - <p> - <em>Percept</em> is an acronym for <em>P</em>ercept - <em>er</em>lang - <em>c</em>oncurr<em>e</em>ncy <em>p</em>rofiling <em>t</em>ool. - </p> - <p> - It is a tool to visualize application level concurrency and - identify concurrency bottlenecks. - </p> - </description> - <xi:include href="percept_ug.xml"/> - <xi:include href="egd_ug.xml"/> -</part> - diff --git a/lib/percept/doc/src/part_notes.xml b/lib/percept/doc/src/part_notes.xml deleted file mode 100644 index f428b4fd81..0000000000 --- a/lib/percept/doc/src/part_notes.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE part SYSTEM "part.dtd"> - -<part xmlns:xi="http://www.w3.org/2001/XInclude"> - <header> - <copyright> - <year>2007</year> - <year>2016</year> - <holder>Ericsson AB, All Rights Reserved</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - </legalnotice> - - <title>Percept Release Notes</title> - <prepared>Björn-Egil Dahlberg</prepared> - <docno></docno> - <date>>2007-11-02</date> - <rev></rev> - <file>part_notes.xml</file> - </header> - <description> - <p> - The <em>Percept</em> application. - </p> - </description> - <xi:include href="notes.xml"/> -</part> - diff --git a/lib/percept/doc/src/percept_compare.gif b/lib/percept/doc/src/percept_compare.gif Binary files differdeleted file mode 100644 index 1c8ccf0186..0000000000 --- a/lib/percept/doc/src/percept_compare.gif +++ /dev/null diff --git a/lib/percept/doc/src/percept_examples.html b/lib/percept/doc/src/percept_examples.html deleted file mode 100644 index df2f52bdfd..0000000000 --- a/lib/percept/doc/src/percept_examples.html +++ /dev/null @@ -1,11 +0,0 @@ -<meta http-equiv="Context-Type" content="text/html; charset=iso-8859-1"> -<?xml version="1.0" encoding="iso-8859-1"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd "> -<html xmlns="http://www.w3.org/1999/xhtml" ><head> -<title>Customization functions</title> -<link rel="stylesheet" type="text/css" href="stylesheet.css"> -</head> -<body> -<h1>Customization functions</h1> -</body> -</html> diff --git a/lib/percept/doc/src/percept_overview.gif b/lib/percept/doc/src/percept_overview.gif Binary files differdeleted file mode 100644 index 12ac172472..0000000000 --- a/lib/percept/doc/src/percept_overview.gif +++ /dev/null diff --git a/lib/percept/doc/src/percept_processes.gif b/lib/percept/doc/src/percept_processes.gif Binary files differdeleted file mode 100644 index 640ff50ee2..0000000000 --- a/lib/percept/doc/src/percept_processes.gif +++ /dev/null diff --git a/lib/percept/doc/src/percept_processinfo.gif b/lib/percept/doc/src/percept_processinfo.gif Binary files differdeleted file mode 100644 index 00cc05f5c9..0000000000 --- a/lib/percept/doc/src/percept_processinfo.gif +++ /dev/null diff --git a/lib/percept/doc/src/percept_ug.xmlsrc b/lib/percept/doc/src/percept_ug.xmlsrc deleted file mode 100644 index 0d243cdabe..0000000000 --- a/lib/percept/doc/src/percept_ug.xmlsrc +++ /dev/null @@ -1,223 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2007</year> - <year>2016</year> - <holder>Ericsson AB, All Rights Reserved</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - </legalnotice> - - <title>Percept</title> - <prepared>Björn-Egil Dahlberg</prepared> - <docno></docno> - <date>2007-11-02</date> - <rev>A</rev> - <file>percept_ug.xml</file> - </header> - <p> - Percept, or Percept - Erlang Concurrency Profiling Tool, utilizes trace - informations and profiler events to form a picture of the processes's and - ports runnability. - </p> - - <section> - <title>Introduction</title> - <p> - Percept uses <c>erlang:trace/3</c> and <c>erlang:system_profile/2</c> to monitor events from - process states. Such states are,</p> - <list> - <item>waiting</item> - <item>running</item> - <item>runnable</item> - <item>free</item> - <item>exiting</item> - </list> - <p> - There are some other states too, <c>suspended</c>, <c>hibernating</c>, and - garbage collecting (<c>gc</c>). The only ignored state is <c>gc</c> and a process is considered to have - its previous state through out the entire garbage collecting phase. The main reason for this, is that our - model considers the <c>gc</c> as a third state neither active nor inactive. - </p> - <p> - A waiting or suspended process is considered an inactive process and a running or - runnable process is considered an active process. - </p> - <p> - Events are collected and stored to a file. The file can be moved and - analyzed on a different machine than the target machine. - </p> - <p> - Note, even if percept is not installed on your target machine, profiling - can still be done via the module <seealso marker="percept_profile">percept_profile</seealso> - located in runtime_tools. - </p> - </section> - <section> - <title>Getting started</title> - <section> - <title>Profiling</title> - <p> - There are a few ways to start the profiling of a specific code. The - command <c>percept:profile/3</c> is a preferred way. - </p> - <p> - The command takes a filename for the data destination file as first - argument, a callback entry-point as second argument and a - list of specific profiler options, for instance <c>procs</c>, as third - argument. - </p> - <p> - Let's say we have a module called example that initializes our - profiling-test and let it run under some defined manner designed by ourself. - The module needs a start function, let's call it go and it takes zero arguments. - The start arguments would look like: - </p> - <p><c>percept:profile("test.dat", {test, go, []}, [procs]).</c></p> - <p> - For a semi-real example we start a tree of processes that does sorting - of random numbers. In our model below we use a controller process that - distributes work to different client processes. - </p> - <codeinclude file="sorter.erl" tag="" type="none"></codeinclude> - <p>We can now start our test using percept:</p> - <pre> -Erlang (BEAM) emulator version 5.6 [async-threads:0] [kernel-poll:false] - -Eshell V5.6 (abort with ^G) -1> percept:profile("test.dat", {sorter, go, [5, 2000, 15]}, [procs]). -Starting profiling. -ok - </pre> - <p> - Percept sets up the trace and profiling facilities to listen for process - specific events. It then stores these events to the <c>test.dat</c> - file. The profiling will go on for the whole duration until - <c>sorter:go/3</c> returns and the profiling has concluded. - </p> - </section> - <section> - <title>Data viewing</title> - <p> - To analyze this file, use <c>percept:analyze("test.dat")</c>. We can do - this on any machine with Percept installed. The command will parse the - data file and insert all events in a RAM database, <c>percept_db</c>. The - initial command will only prompt how many processes were involved in the - profile. - </p> - <pre> -2> percept:analyze("test.dat"). -Parsing: "test.dat" -Parsed 428 entries in 3.81310e-2 s. - 17 created processes. - 0 opened ports. -ok - </pre> - <p> - To view the data we start the web-server using - <c>percept:start_webserver/1</c>. The command will return the hostname - and the a port where we should direct our favorite web browser. - </p> - <pre> -3> percept:start_webserver(8888). -{started,"durin",8888} -4> - </pre> - <section> - <title>Overview selection</title> - <p> - Now we can view our data. The database has its content from - <c>percept:analyze/1</c> command and the webserver is started. - </p> - <p> - When we click on the <c>overview</c> button in the menu percept will - generate a graph of the concurrency and send it to our web browser. In this - view we get no details but rather the big picture. We can see if - our processes behave in an inefficient manner. Dips in the graph represents - low concurrency in the erlang system. - </p> - <p> - We can zoom in on different areas of the graph either using the mouse - to select an area or by specifying min and max ranges in the edit boxes. - </p> - <note> - <p>Measured time is presented in seconds if nothing else is stated.</p> - </note> - <image file="percept_overview.gif"> - <icaption>Overview selection</icaption> - </image> - </section> - <section> - <title>Processes selection</title> - <p> - To get a more detailed description we can select the process view by - clicking the <c>processes</c> button in the menu. - </p> - <p> - The table shows process id's that are click-able and direct you to - the process information page, a lifetime bar that presents a rough estimate - in green color about when the process was alive during profiling, an - entry-point, its registered name if it had one and the process's - parent id. - </p> - <p> - We can select which processes we want to compare and then hit the - <c>compare</c> button on the top right of the screen. - </p> - <image file="percept_processes.gif"> - <icaption>Processes selection</icaption> - </image> - </section> - <section> - <title>Compare selection</title> - <p> - The activity bar under the concurrency graph shows each process's - runnability. The color green shows when a process is active (which is - running or runnable) and the white color represents time when a - process is inactive (waiting in a receive or is suspended). - </p> - <p> - To inspect a certain process click on the process id button, this will - direct you to a process information page for that specific process. - </p> - <image file="percept_compare.gif"> - <icaption>Processes compare selection</icaption> - </image> - </section> - <section> - <title>Process information selection</title> - <p> - Here we can some general information for the process. Parent and - children processes, spawn and exit times, entry-point and start arguments. - </p> - <p> - We can also see the process' inactive times. How many times it has - been waiting, statistical information and most importantly in which - function. - </p> - <p> - The time percentages presented in process information are of time spent in waiting, not total run time. - </p> - <image file="percept_processinfo.gif"> - <icaption>Process information selection</icaption> - </image> - </section> - </section> - </section> -</chapter> diff --git a/lib/percept/doc/src/ref_man.xml b/lib/percept/doc/src/ref_man.xml deleted file mode 100644 index 143312489b..0000000000 --- a/lib/percept/doc/src/ref_man.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE application SYSTEM "application.dtd"> - -<application xmlns:xi="http://www.w3.org/2001/XInclude"> - <header> - <copyright> - <year>2007</year> - <year>2016</year> - <holder>Ericsson AB, All Rights Reserved</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - </legalnotice> - - <title>Percept Reference Manual</title> - <prepared>Edoc</prepared> - <docno></docno> - <date>2007-11-02</date> - <rev>1.0</rev> - <file>ref_man.xml</file> - </header> - <description> - <p> - <em>Percept</em> is an acronym for <em>P</em>ercept - <em>er</em>lang - <em>c</em>oncurr<em>e</em>ncy <em>p</em>rofiling <em>t</em>ool. - </p> - <p> - It is a tool to visualize application level concurrency and - identify concurrency bottlenecks. - </p> - </description> - <xi:include href="egd.xml"/> - <xi:include href="percept.xml"/> - <xi:include href="percept_profile.xml"/> -</application> - diff --git a/lib/percept/doc/src/sorter.erl b/lib/percept/doc/src/sorter.erl deleted file mode 100644 index 8d5f2c715c..0000000000 --- a/lib/percept/doc/src/sorter.erl +++ /dev/null @@ -1,41 +0,0 @@ --module(sorter). --export([go/3,loop/0,main/4]). - -go(I,N,M) -> - spawn(?MODULE, main, [I,N,M,self()]), - receive done -> ok end. - -main(I,N,M,Parent) -> - Pids = lists:foldl( - fun(_,Ps) -> - [ spawn(?MODULE,loop, []) | Ps] - end, [], lists:seq(1,M)), - - lists:foreach( - fun(_) -> - send_work(N,Pids), - gather(Pids) - end, lists:seq(1,I)), - - lists:foreach( - fun(Pid) -> - Pid ! {self(), quit} - end, Pids), - - gather(Pids), Parent ! done. - -send_work(_,[]) -> ok; -send_work(N,[Pid|Pids]) -> - Pid ! {self(),sort,N}, - send_work(round(N*1.2),Pids). - -loop() -> - receive - {Pid, sort, N} -> dummy_sort(N),Pid ! {self(), done},loop(); - {Pid, quit} -> Pid ! {self(), done} - end. - -dummy_sort(N) -> lists:sort([ random:uniform(N) || _ <- lists:seq(1,N)]). - -gather([]) -> ok; -gather([Pid|Pids]) -> receive {Pid, done} -> gather(Pids) end. diff --git a/lib/percept/doc/src/test1.gif b/lib/percept/doc/src/test1.gif Binary files differdeleted file mode 100644 index 70a519d8e3..0000000000 --- a/lib/percept/doc/src/test1.gif +++ /dev/null diff --git a/lib/percept/doc/src/test2.gif b/lib/percept/doc/src/test2.gif Binary files differdeleted file mode 100644 index f18e1f9e58..0000000000 --- a/lib/percept/doc/src/test2.gif +++ /dev/null diff --git a/lib/percept/doc/src/test3.gif b/lib/percept/doc/src/test3.gif Binary files differdeleted file mode 100644 index c7581f19aa..0000000000 --- a/lib/percept/doc/src/test3.gif +++ /dev/null diff --git a/lib/percept/doc/src/test4.gif b/lib/percept/doc/src/test4.gif Binary files differdeleted file mode 100644 index e7d52c08a3..0000000000 --- a/lib/percept/doc/src/test4.gif +++ /dev/null diff --git a/lib/percept/doc/stylesheet.css b/lib/percept/doc/stylesheet.css deleted file mode 100644 index 24d8a02145..0000000000 --- a/lib/percept/doc/stylesheet.css +++ /dev/null @@ -1,39 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2007-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -BODY {color: #000000; - background-color: #ffffff; - margin-left: .4in} -H1 {margin-left: -.4in} -H2 {margin-left: -.4in} -H3 {margin-left: -.2in} -.logo{float:right;} -.toc UL { - list-style-type: none; - border: solid; - border-width: thin; - padding-left: 10px; - padding-right: 10px; - padding-top: 5px; - padding-bottom: 5px; - background: #f0f0f0; - letter-spacing: 2px; - line-height: 20px; -} diff --git a/lib/percept/ebin/.gitignore b/lib/percept/ebin/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/ebin/.gitignore +++ /dev/null diff --git a/lib/percept/include/.gitignore b/lib/percept/include/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/include/.gitignore +++ /dev/null diff --git a/lib/percept/info b/lib/percept/info deleted file mode 100644 index 07d58d28ae..0000000000 --- a/lib/percept/info +++ /dev/null @@ -1,2 +0,0 @@ -group: tools -short: A concurrency profiler tool. diff --git a/lib/percept/priv/Makefile b/lib/percept/priv/Makefile deleted file mode 100644 index a1912edfc0..0000000000 --- a/lib/percept/priv/Makefile +++ /dev/null @@ -1,97 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2007-2016. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# %CopyrightEnd% -# - -include $(ERL_TOP)/make/target.mk -include $(ERL_TOP)/make/$(TARGET)/otp.mk - -# ---------------------------------------------------- -# Application version -# ---------------------------------------------------- -include ../vsn.mk -VSN=$(PERCEPT_VSN) - -# ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/lib/percept-$(VSN) - -# ---------------------------------------------------- -# Target Specs -# ---------------------------------------------------- -CONF_FILES = \ - server_root/conf/mime.types - -HTDOCS_FILES = \ - server_root/htdocs/index.html - -IMAGE_FILES = \ - server_root/images/nav.png \ - server_root/images/white.png - -SCRIPT_FILES = \ - server_root/scripts/percept_area_select.js \ - server_root/scripts/percept_error_handler.js \ - server_root/scripts/percept_select_all.js - -CSS_FILES = \ - server_root/css/percept.css - -FONT_FILES = \ - fonts/6x11_latin1.wingsfont - -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -ERL_COMPILE_FLAGS += - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- - -debug opt: - -clean: - -docs: - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_spec: opt - # Finished - $(INSTALL_DIR) "$(RELSYSDIR)/priv/logs" - $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root" - $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/htdocs" - $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/priv/server_root/htdocs" - $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/conf" - $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/priv/server_root/conf" - $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/scripts" - $(INSTALL_DATA) $(SCRIPT_FILES) "$(RELSYSDIR)/priv/server_root/scripts" - $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/css" - $(INSTALL_DATA) $(CSS_FILES) "$(RELSYSDIR)/priv/server_root/css" - $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/images" - $(INSTALL_DATA) $(IMAGE_FILES) "$(RELSYSDIR)/priv/server_root/images" - $(INSTALL_DIR) "$(RELSYSDIR)/priv/fonts" - $(INSTALL_DATA) $(FONT_FILES) "$(RELSYSDIR)/priv/fonts" - -release_docs_spec: - diff --git a/lib/percept/priv/fonts/6x11_latin1.wingsfont b/lib/percept/priv/fonts/6x11_latin1.wingsfont Binary files differdeleted file mode 100644 index d1e1c42eef..0000000000 --- a/lib/percept/priv/fonts/6x11_latin1.wingsfont +++ /dev/null diff --git a/lib/percept/priv/logs/.gitignore b/lib/percept/priv/logs/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/priv/logs/.gitignore +++ /dev/null diff --git a/lib/percept/priv/obj/.gitignore b/lib/percept/priv/obj/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/priv/obj/.gitignore +++ /dev/null diff --git a/lib/percept/priv/server_root/cgi-bin/.gitignore b/lib/percept/priv/server_root/cgi-bin/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/percept/priv/server_root/cgi-bin/.gitignore +++ /dev/null diff --git a/lib/percept/priv/server_root/conf/mime.types b/lib/percept/priv/server_root/conf/mime.types deleted file mode 100644 index 6245efdbd9..0000000000 --- a/lib/percept/priv/server_root/conf/mime.types +++ /dev/null @@ -1,462 +0,0 @@ -application/EDI-Consent -application/EDI-X12 -application/EDIFACT -application/activemessage -application/andrew-inset ez -application/applefile -application/atomicmail -application/batch-SMTP -application/beep+xml -application/cals-1840 -application/commonground -application/cybercash -application/dca-rft -application/dec-dx -application/dvcs -application/eshop -application/http -application/hyperstudio -application/iges -application/index -application/index.cmd -application/index.obj -application/index.response -application/index.vnd -application/iotp -application/ipp -application/isup -application/font-tdpfr -application/mac-binhex40 hqx -application/mac-compactpro cpt -application/macwriteii -application/marc -application/mathematica -application/mathematica-old -application/msword doc -application/news-message-id -application/news-transmission -application/ocsp-request -application/ocsp-response -application/octet-stream bin dms lha lzh exe class so dll -application/oda oda -application/parityfec -application/pdf pdf -application/pgp-encrypted -application/pgp-keys -application/pgp-signature -application/pkcs10 -application/pkcs7-mime -application/pkcs7-signature -application/pkix-cert -application/pkix-crl -application/pkixcmp -application/postscript ai eps ps -application/prs.alvestrand.titrax-sheet -application/prs.cww -application/prs.nprend -application/qsig -application/remote-printing -application/riscos -application/rtf -application/sdp -application/set-payment -application/set-payment-initiation -application/set-registration -application/set-registration-initiation -application/sgml -application/sgml-open-catalog -application/sieve -application/slate -application/smil smi smil -application/timestamp-query -application/timestamp-reply -application/vemmi -application/vnd.3M.Post-it-Notes -application/vnd.FloGraphIt -application/vnd.accpac.simply.aso -application/vnd.accpac.simply.imp -application/vnd.acucobol -application/vnd.aether.imp -application/vnd.anser-web-certificate-issue-initiation -application/vnd.anser-web-funds-transfer-initiation -application/vnd.audiograph -application/vnd.businessobjects -application/vnd.bmi -application/vnd.canon-cpdl -application/vnd.canon-lips -application/vnd.claymore -application/vnd.commerce-battelle -application/vnd.commonspace -application/vnd.comsocaller -application/vnd.contact.cmsg -application/vnd.cosmocaller -application/vnd.cups-postscript -application/vnd.cups-raster -application/vnd.cups-raw -application/vnd.ctc-posml -application/vnd.cybank -application/vnd.dna -application/vnd.dpgraph -application/vnd.dxr -application/vnd.ecdis-update -application/vnd.ecowin.chart -application/vnd.ecowin.filerequest -application/vnd.ecowin.fileupdate -application/vnd.ecowin.series -application/vnd.ecowin.seriesrequest -application/vnd.ecowin.seriesupdate -application/vnd.enliven -application/vnd.epson.esf -application/vnd.epson.msf -application/vnd.epson.quickanime -application/vnd.epson.salt -application/vnd.epson.ssf -application/vnd.ericsson.quickcall -application/vnd.eudora.data -application/vnd.fdf -application/vnd.ffsns -application/vnd.framemaker -application/vnd.fsc.weblaunch -application/vnd.fujitsu.oasys -application/vnd.fujitsu.oasys2 -application/vnd.fujitsu.oasys3 -application/vnd.fujitsu.oasysgp -application/vnd.fujitsu.oasysprs -application/vnd.fujixerox.ddd -application/vnd.fujixerox.docuworks -application/vnd.fujixerox.docuworks.binder -application/vnd.fut-misnet -application/vnd.grafeq -application/vnd.groove-account -application/vnd.groove-identity-message -application/vnd.groove-injector -application/vnd.groove-tool-message -application/vnd.groove-tool-template -application/vnd.groove-vcard -application/vnd.hhe.lesson-player -application/vnd.hp-HPGL -application/vnd.hp-PCL -application/vnd.hp-PCLXL -application/vnd.hp-hpid -application/vnd.hp-hps -application/vnd.httphone -application/vnd.hzn-3d-crossword -application/vnd.ibm.afplinedata -application/vnd.ibm.MiniPay -application/vnd.ibm.modcap -application/vnd.informix-visionary -application/vnd.intercon.formnet -application/vnd.intertrust.digibox -application/vnd.intertrust.nncp -application/vnd.intu.qbo -application/vnd.intu.qfx -application/vnd.irepository.package+xml -application/vnd.is-xpr -application/vnd.japannet-directory-service -application/vnd.japannet-jpnstore-wakeup -application/vnd.japannet-payment-wakeup -application/vnd.japannet-registration -application/vnd.japannet-registration-wakeup -application/vnd.japannet-setstore-wakeup -application/vnd.japannet-verification -application/vnd.japannet-verification-wakeup -application/vnd.koan -application/vnd.lotus-1-2-3 -application/vnd.lotus-approach -application/vnd.lotus-freelance -application/vnd.lotus-notes -application/vnd.lotus-organizer -application/vnd.lotus-screencam -application/vnd.lotus-wordpro -application/vnd.mcd -application/vnd.mediastation.cdkey -application/vnd.meridian-slingshot -application/vnd.mif mif -application/vnd.minisoft-hp3000-save -application/vnd.mitsubishi.misty-guard.trustweb -application/vnd.mobius.daf -application/vnd.mobius.dis -application/vnd.mobius.msl -application/vnd.mobius.plc -application/vnd.mobius.txf -application/vnd.motorola.flexsuite -application/vnd.motorola.flexsuite.adsi -application/vnd.motorola.flexsuite.fis -application/vnd.motorola.flexsuite.gotap -application/vnd.motorola.flexsuite.kmr -application/vnd.motorola.flexsuite.ttc -application/vnd.motorola.flexsuite.wem -application/vnd.mozilla.xul+xml -application/vnd.ms-artgalry -application/vnd.ms-asf -application/vnd.ms-excel xls -application/vnd.ms-lrm -application/vnd.ms-powerpoint ppt -application/vnd.ms-project -application/vnd.ms-tnef -application/vnd.ms-works -application/vnd.mseq -application/vnd.msign -application/vnd.music-niff -application/vnd.musician -application/vnd.netfpx -application/vnd.noblenet-directory -application/vnd.noblenet-sealer -application/vnd.noblenet-web -application/vnd.novadigm.EDM -application/vnd.novadigm.EDX -application/vnd.novadigm.EXT -application/vnd.osa.netdeploy -application/vnd.palm -application/vnd.pg.format -application/vnd.pg.osasli -application/vnd.powerbuilder6 -application/vnd.powerbuilder6-s -application/vnd.powerbuilder7 -application/vnd.powerbuilder7-s -application/vnd.powerbuilder75 -application/vnd.powerbuilder75-s -application/vnd.previewsystems.box -application/vnd.publishare-delta-tree -application/vnd.pvi.ptid1 -application/vnd.pwg-xhtml-print+xml -application/vnd.rapid -application/vnd.s3sms -application/vnd.seemail -application/vnd.shana.informed.formdata -application/vnd.shana.informed.formtemplate -application/vnd.shana.informed.interchange -application/vnd.shana.informed.package -application/vnd.sss-cod -application/vnd.sss-dtf -application/vnd.sss-ntf -application/vnd.street-stream -application/vnd.svd -application/vnd.swiftview-ics -application/vnd.triscape.mxs -application/vnd.trueapp -application/vnd.truedoc -application/vnd.tve-trigger -application/vnd.ufdl -application/vnd.uplanet.alert -application/vnd.uplanet.alert-wbxml -application/vnd.uplanet.bearer-choice-wbxml -application/vnd.uplanet.bearer-choice -application/vnd.uplanet.cacheop -application/vnd.uplanet.cacheop-wbxml -application/vnd.uplanet.channel -application/vnd.uplanet.channel-wbxml -application/vnd.uplanet.list -application/vnd.uplanet.list-wbxml -application/vnd.uplanet.listcmd -application/vnd.uplanet.listcmd-wbxml -application/vnd.uplanet.signal -application/vnd.vcx -application/vnd.vectorworks -application/vnd.vidsoft.vidconference -application/vnd.visio -application/vnd.vividence.scriptfile -application/vnd.wap.sic -application/vnd.wap.slc -application/vnd.wap.wbxml wbxml -application/vnd.wap.wmlc wmlc -application/vnd.wap.wmlscriptc wmlsc -application/vnd.webturbo -application/vnd.wrq-hp3000-labelled -application/vnd.wt.stf -application/vnd.xara -application/vnd.xfdl -application/vnd.yellowriver-custom-menu -application/whoispp-query -application/whoispp-response -application/wita -application/wordperfect5.1 -application/x-bcpio bcpio -application/x-cdlink vcd -application/x-chess-pgn pgn -application/x-compress -application/x-cpio cpio -application/x-csh csh -application/x-director dcr dir dxr -application/x-dvi dvi -application/x-futuresplash spl -application/x-gtar gtar -application/x-gzip -application/x-hdf hdf -application/x-javascript js -application/x-koan skp skd skt skm -application/x-latex latex -application/x-netcdf nc cdf -application/x-sh sh -application/x-shar shar -application/x-shockwave-flash swf -application/x-stuffit sit -application/x-sv4cpio sv4cpio -application/x-sv4crc sv4crc -application/x-tar tar -application/x-tcl tcl -application/x-tex tex -application/x-texinfo texinfo texi -application/x-troff t tr roff -application/x-troff-man man -application/x-troff-me me -application/x-troff-ms ms -application/x-ustar ustar -application/x-wais-source src -application/x400-bp -application/xml -application/xml-dtd -application/xml-external-parsed-entity -application/zip zip -audio/32kadpcm -audio/basic au snd -audio/g.722.1 -audio/l16 -audio/midi mid midi kar -audio/mp4a-latm -audio/mpa-robust -audio/mpeg mpga mp2 mp3 -audio/parityfec -audio/prs.sid -audio/telephone-event -audio/tone -audio/vnd.cisco.nse -audio/vnd.cns.anp1 -audio/vnd.cns.inf1 -audio/vnd.digital-winds -audio/vnd.everad.plj -audio/vnd.lucent.voice -audio/vnd.nortel.vbk -audio/vnd.nuera.ecelp4800 -audio/vnd.nuera.ecelp7470 -audio/vnd.nuera.ecelp9600 -audio/vnd.octel.sbc -audio/vnd.qcelp -audio/vnd.rhetorex.32kadpcm -audio/vnd.vmx.cvsd -audio/x-aiff aif aiff aifc -audio/x-mpegurl m3u -audio/x-pn-realaudio ram rm -audio/x-pn-realaudio-plugin rpm -audio/x-realaudio ra -audio/x-wav wav -chemical/x-pdb pdb -chemical/x-xyz xyz -image/bmp bmp -image/cgm -image/g3fax -image/gif gif -image/ief ief -image/jpeg jpeg jpg jpe -image/naplps -image/png png -image/prs.btif -image/prs.pti -image/tiff tiff tif -image/vnd.cns.inf2 -image/vnd.dwg -image/vnd.dxf -image/vnd.fastbidsheet -image/vnd.fpx -image/vnd.fst -image/vnd.fujixerox.edmics-mmr -image/vnd.fujixerox.edmics-rlc -image/vnd.mix -image/vnd.net-fpx -image/vnd.svf -image/vnd.wap.wbmp wbmp -image/vnd.xiff -image/x-cmu-raster ras -image/x-portable-anymap pnm -image/x-portable-bitmap pbm -image/x-portable-graymap pgm -image/x-portable-pixmap ppm -image/x-rgb rgb -image/x-xbitmap xbm -image/x-xpixmap xpm -image/x-xwindowdump xwd -message/delivery-status -message/disposition-notification -message/external-body -message/http -message/news -message/partial -message/rfc822 -message/s-http -model/iges igs iges -model/mesh msh mesh silo -model/vnd.dwf -model/vnd.flatland.3dml -model/vnd.gdl -model/vnd.gs-gdl -model/vnd.gtw -model/vnd.mts -model/vnd.vtu -model/vrml wrl vrml -multipart/alternative -multipart/appledouble -multipart/byteranges -multipart/digest -multipart/encrypted -multipart/form-data -multipart/header-set -multipart/mixed -multipart/parallel -multipart/related -multipart/report -multipart/signed -multipart/voice-message -text/calendar -text/css css -text/directory -text/enriched -text/html html htm -text/parityfec -text/plain asc txt -text/prs.lines.tag -text/rfc822-headers -text/richtext rtx -text/rtf rtf -text/sgml sgml sgm -text/tab-separated-values tsv -text/t140 -text/uri-list -text/vnd.DMClientScript -text/vnd.IPTC.NITF -text/vnd.IPTC.NewsML -text/vnd.abc -text/vnd.curl -text/vnd.flatland.3dml -text/vnd.fly -text/vnd.fmi.flexstor -text/vnd.in3d.3dml -text/vnd.in3d.spot -text/vnd.latex-z -text/vnd.motorola.reflex -text/vnd.ms-mediapackage -text/vnd.wap.si -text/vnd.wap.sl -text/vnd.wap.wml wml -text/vnd.wap.wmlscript wmls -text/x-setext etx -text/x-server-parsed-html shtml -text/xml xml xsl -text/xml-external-parsed-entity -video/mp4v-es -video/mpeg mpeg mpg mpe -video/parityfec -video/pointer -video/quicktime qt mov -video/vnd.fvt -video/vnd.motorola.video -video/vnd.motorola.videop -video/vnd.mpegurl mxu -video/vnd.mts -video/vnd.nokia.interleaved-multimedia -video/vnd.vivo -video/x-msvideo avi -video/x-sgi-movie movie -x-conference/x-cooltalk ice - - - diff --git a/lib/percept/priv/server_root/css/percept.css b/lib/percept/priv/server_root/css/percept.css deleted file mode 100644 index 2d0734b6b6..0000000000 --- a/lib/percept/priv/server_root/css/percept.css +++ /dev/null @@ -1,162 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2007-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* Globals */ -html, body { - margin: 0; - padding: 0; - font: 12px Verdana; - background: #7a83a2; -} - -table { - border-collapse: collapse; - /*width: 100%;*/ -} - -tr.even { - background-color: #ffffff; color: black; -} - -tr.odd { - background-color: #def2ef; color: black; -} - -td { - text-valign: top; - text-align: right; - font: 14px Verdana; -} - -th { - letter-spacing: 2px; - text-align: right; - padding: 4px 4px 4px 8px; -} - -a { - color: yellow; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -td a { - color: #101010; -} - -img { - border: 0; -} - - -/* Header and footer stuff */ - -#header { - font: bold 24px Verdana; - padding-left: 156px; - padding-right: 156px; - padding-top: 10px; - padding-bottom: 10px; - height: 74px; - text-align: right; - background: #7a83a2; -} - -#footer { - font: 12px Verdana; - position: relative; - padding: 5px; - border-top: 1px solid black; - clear:left; -} - - -/* Content stuff */ - -#content { - background: #fefefe; - position: relative; - padding: 5px 25px 5px 25px; - margin: 0px 60px 0px 60px; - border-top: 1px solid #383a32; - border-left: 1px solid #383a32; - border-right: 1px solid #383a32; - border-bottom: 1px solid #383a32; -} - -.table_header { - font-decoration: underline; - width: 100%; -} - -/* Menu */ - -#menu { - margin: 0px 60px 0px 60px; - height: 30px; - padding-right: 0px; - background-image: url('../images/nav.png'); - background-repeat: repeat-x; - padding-top: 0px; - border-top: 1px solid #383a32; - border-left: 1px solid #383a32; - border-right: 1px solid #383a32; -} - -.menu_tabs { - overflow: hidden; -} - -.menu_tabs ul { - margin: 0; - padding: 0; - font: bold 12px Verdana; - list-style-type: none; -} - -.menu_tabs li { - display: inline; - margin: 0; - background-repeat: repeat-x; -} - -.menu_tabs li a { - float: right; - display: block; - text-decoration: none; - margin: 0; - padding: 8px; 7px 8px; 3px; - border-left: 1px solid #777487; -} - -.menu_tabs li a:visited { - color: black; -} - -.menu_tabs li a:hover { - background: #cae8ea; -} - -.menu_tabs li a:selected .menu_tabs li a:active { - background: yellow; -} diff --git a/lib/percept/priv/server_root/htdocs/index.html b/lib/percept/priv/server_root/htdocs/index.html deleted file mode 100644 index f7322cba89..0000000000 --- a/lib/percept/priv/server_root/htdocs/index.html +++ /dev/null @@ -1,41 +0,0 @@ -<!-- - %CopyrightBegin% - - Copyright Ericsson AB 2007-2016. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - %CopyrightEnd% ---> -<html> -<head> - <title>percept</title> - <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1" /> - <link href="/css/percept.css" rel="stylesheet" type="text/css"> -</head> - -<body> - <div id="header"><a href=/index.html>percept</a></div> - <div id="menu" class="menu_tabs"> - <ul> - <li><a href=/cgi-bin/percept_html/databases_page>databases</a></li> - <li><a href=/cgi-bin/percept_html/processes_page>processes</a></li> - <li><a href=/cgi-bin/percept_html/page>overview</a></li> - </ul> - </div> - <div id="content"> - <p>Percept - Erlang Concurrency Profiling Tool</p> - </div> -</body> -</html> - diff --git a/lib/percept/priv/server_root/images/nav.png b/lib/percept/priv/server_root/images/nav.png Binary files differdeleted file mode 100644 index d136e806b1..0000000000 --- a/lib/percept/priv/server_root/images/nav.png +++ /dev/null diff --git a/lib/percept/priv/server_root/images/white.png b/lib/percept/priv/server_root/images/white.png Binary files differdeleted file mode 100644 index 94381b429d..0000000000 --- a/lib/percept/priv/server_root/images/white.png +++ /dev/null diff --git a/lib/percept/priv/server_root/scripts/percept_area_select.js b/lib/percept/priv/server_root/scripts/percept_area_select.js deleted file mode 100644 index 83fbb02c92..0000000000 --- a/lib/percept/priv/server_root/scripts/percept_area_select.js +++ /dev/null @@ -1,182 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2007-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -function size_image(img, src) { - percept_content = document.getElementById("content"); - var width = percept_content.offsetWidth - 120; - var imgfile = "/cgi-bin/percept_graph/" + src + "&width=" + width; - img.src = imgfile; - img.onload = ''; -} - -function load_image() { - var percept_graph = document.getElementById("percept_graph"); - if (percept_graph) { - percept_content = document.getElementById("content"); - var width = percept_content.offsetWidth - 50; - var height = max(screen.height - 550, 600); - var rmin = document.form_area.data_min.value; - var rmax = document.form_area.data_max.value; - - percept_graph.style.backgroundImage = "url('/cgi-bin/percept_graph/graph" + - "?range_min=" + rmin + - "&range_max=" + rmax + - "&width=" + width + - "&height=" + height + "')"; - percept_graph.style.width = width; - percept_graph.style.height = height; - } -} - -function select_image() { - var Graph = document.getElementById("percept_graph"); - if (Graph) { - var GraphIndex = document.form_area.graph_select.selectedIndex; - var GraphSelectValue = document.form_area.graph_select.options[GraphIndex].value; - Graph.style.backgroundImage = "url('" + GraphSelectValue +"')"; - } -} - -function select_down(event) { - var Graf = document.getElementById("percept_graph"); - var Area = document.getElementById("percept_areaselect"); - var x = event.offsetX?(event.offsetX):event.pageX-Graf.offsetLeft; - x = x - 60; - - var width = Graf.offsetWidth; - var height = Graf.offsetHeight; - var margin = 20; - - var Xmin = document.form_area.data_min.value; - var Xmax = document.form_area.data_max.value; - - // Trim edges - - if ( x < margin ) { - x = margin; - } - - if ( x > width - margin ) { - x = width - margin; - } - - Area.style.left = x; - Area.style.top = height - margin; - Area.style.width = 1; - Area.style.height = margin; - Area.moving = true; - Area.bgcolor = "#00ff00"; - Area.style.visibility = "visible"; - Area.style.borderRight = "1px solid #000" - Area.style.borderLeft = "1px solid #000" - Area.style.opacity = 0.65; - Area.style.filter = 'alpha(opacity=65)'; - var RangeMin = convert_image2graph(x, Xmin, Xmax, margin, width - margin); - if (RangeMin == 0) document.form_area.range_min.value = 0.0; - else document.form_area.range_min.value = RangeMin; -} - - function select_move(event) { - var Graf = document.getElementById("percept_graph"); - var Area = document.getElementById("percept_areaselect"); - var x = event.offsetX?(event.offsetX):event.pageX-Graf.offsetLeft; - x = x - 60; - if (Area.moving == true) { - - var width = Graf.offsetWidth; - var height = Graf.offsetHeight; - var margin = 20; - var Xmin = document.form_area.data_min.value; - var Xmax = document.form_area.data_max.value; - - // Trim edges - - if ( x < margin ) { - x = margin; - } - - if ( x > width - margin ) { - x = width - margin; - } - - var x0 = min(x, Area.offsetLeft); - var x1 = max(x, Area.offsetLeft); - var w = (x1 - x0); - Area.style.left = x0; - Area.style.width = w; - var RangeMin = convert_image2graph(x0, Xmin, Xmax, margin, width - margin); - var RangeMax = convert_image2graph(x1, Xmin, Xmax, margin, width - margin); - Area.style.visibility = "visible"; - - if (RangeMin == 0) document.form_area.range_min.value = 0.0; - else document.form_area.range_min.value = RangeMin; - if (RangeMax == 0) document.form_area.range_max.value = 0.0; - else document.form_area.range_max.value = RangeMax; - } -} - -function select_up(event) { - var Graf = document.getElementById("percept_graph"); - var Area = document.getElementById("percept_areaselect"); - var x = event.offsetX?(event.offsetX):event.pageX-Graf.offsetLeft; - - x = x - 60; - var width = Graf.offsetWidth; - var height = Graf.offsetHeight; - var margin = 20; - var Xmin = document.form_area.data_min.value; - var Xmax = document.form_area.data_max.value; - - // Trim edges - - if ( x < margin ) { - x = margin; - } - - if ( x > width - margin ) { - x = width - margin; - } - - var w = (x - Area.style.offsetLeft); - - Area.moving = false; - Area.style.width = w; - var RangeMax = convert_image2graph(x, Xmin, Xmax, margin, width - margin); - if (RangeMax == 0) document.form_area.range_max.value = 0.0; - else document.form_area.range_max.value = RangeMax; -} - -function min(A, B) { - if (A > B) return B; - else return A; -} - -function max(A,B) { - if (A > B) return A; - else return B; -} - -function convert_image2graph(X, Xmin, Xmax, X0, X1) { - var ImageWidth = X1 - X0; - var RangeWidth = Xmax - Xmin; - var DX = RangeWidth/ImageWidth; - var Xprime = (X - X0)*DX + Xmin*1.0; - return Xprime; -} diff --git a/lib/percept/priv/server_root/scripts/percept_error_handler.js b/lib/percept/priv/server_root/scripts/percept_error_handler.js deleted file mode 100644 index dad8f2b566..0000000000 --- a/lib/percept/priv/server_root/scripts/percept_error_handler.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2007-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -var onerror=handleErr; - -function handleErr(msg,url,l) { - var txt = "Error: " + msg + "\nURL: " + url + "\nCode line: " + l; - alert(txt); -} diff --git a/lib/percept/priv/server_root/scripts/percept_select_all.js b/lib/percept/priv/server_root/scripts/percept_select_all.js deleted file mode 100644 index c8eb966059..0000000000 --- a/lib/percept/priv/server_root/scripts/percept_select_all.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2007-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -function selectall() { - for (var i = 0; i < document.process_select.elements.length; i++) { - var e = document.process_select.elements[i]; - if ((e.name != 'select_all') && (e.type == 'checkbox')) { - e.checked = document.process_select.select_all.checked; - } - } -} diff --git a/lib/percept/src/Makefile b/lib/percept/src/Makefile deleted file mode 100644 index b2ec87d08c..0000000000 --- a/lib/percept/src/Makefile +++ /dev/null @@ -1,108 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2007-2016. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# %CopyrightEnd% - -include $(ERL_TOP)/make/target.mk -include $(ERL_TOP)/make/$(TARGET)/otp.mk - -# ---------------------------------------------------- -# Application version -# ---------------------------------------------------- -include ../vsn.mk -VSN=$(PERCEPT_VSN) - -# ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/lib/percept-$(VSN) - -# ---------------------------------------------------- -# Common Macros -# ---------------------------------------------------- - -MODULES= \ - egd \ - egd_png \ - egd_font \ - egd_render \ - egd_primitives \ - percept \ - percept_db \ - percept_html \ - percept_image \ - percept_graph \ - percept_analyzer - - -#HRL_FILES= ../include/ - -INTERNAL_HRL_FILES= egd.hrl percept.hrl - -ERL_FILES= $(MODULES:%=%.erl) - -TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) - -APP_FILE= percept.app - -APP_SRC= $(APP_FILE).src -APP_TARGET= $(EBIN)/$(APP_FILE) - -APPUP_FILE= percept.appup - -APPUP_SRC= $(APPUP_FILE).src -APPUP_TARGET= $(EBIN)/$(APPUP_FILE) - -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -ERL_COMPILE_FLAGS += +warn_unused_vars -I../include - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- - -debug opt: $(TARGET_FILES) - -clean: - rm -f $(TARGET_FILES) - rm -f errs core *~ - -$(APP_TARGET): $(APP_SRC) ../vsn.mk - $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ - -$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk - $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ - -docs: - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_spec: opt - $(INSTALL_DIR) "$(RELSYSDIR)/src" - $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" -# $(INSTALL_DIR) "$(RELSYSDIR)/include" -# $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" - $(INSTALL_DIR) "$(RELSYSDIR)/ebin" - $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" - -release_docs_spec: - diff --git a/lib/percept/src/egd.erl b/lib/percept/src/egd.erl deleted file mode 100644 index fe52da71f1..0000000000 --- a/lib/percept/src/egd.erl +++ /dev/null @@ -1,275 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - -%% -%% @doc egd - erlang graphical drawer -%% -%% - --module(egd). - --export([create/2, destroy/1, information/1]). --export([text/5, line/4, color/1, color/2]). --export([rectangle/4, filledRectangle/4, filledEllipse/4]). --export([arc/4, arc/5]). --export([render/1, render/2, render/3]). - --export([filledTriangle/5, polygon/3]). - --export([save/2]). - --include("egd.hrl"). - -%%========================================================================== -%% Type definitions -%%========================================================================== - -%% @type egd_image() -%% @type font() -%% @type point() = {integer(), integer()} -%% @type color() -%% @type render_option() = {render_engine, opaque} | {render_engine, alpha} - --type egd_image() :: pid(). --type point() :: {non_neg_integer(), non_neg_integer()}. --type render_option() :: {'render_engine', 'opaque'} | {'render_engine', 'alpha'}. --type color() :: {float(), float(), float(), float()}. - -%%========================================================================== -%% Interface functions -%%========================================================================== - -%% @spec create(integer(), integer()) -> egd_image() -%% @doc Creates an image area and returns its reference. - --spec create(Width :: integer(), Height :: integer()) -> egd_image(). - -create(Width,Height) -> - spawn_link(fun() -> init(trunc(Width),trunc(Height)) end). - - -%% @spec destroy(egd_image()) -> ok -%% @doc Destroys the image. - --spec destroy(Image :: egd_image()) -> ok. - -destroy(Image) -> - cast(Image, destroy). - - -%% @spec render(egd_image()) -> binary() -%% @equiv render(Image, png, [{render_engine, opaque}]) - --spec render(Image :: egd_image()) -> binary(). - -render(Image) -> - render(Image, png, [{render_engine, opaque}]). - -%% @spec render(egd_image(), png | raw_bitmap) -> binary() -%% @equiv render(Image, Type, [{render_engine, opaque}]) - -render(Image, Type) -> - render(Image, Type, [{render_engine, opaque}]). - -%% @spec render(egd_image(), png | raw_bitmap, [render_option()]) -> binary() -%% @doc Renders a binary from the primitives specified by egd_image(). The -%% binary can either be a raw bitmap with rgb tripplets or a binary in png -%% format. - --spec render( - Image :: egd_image(), - Type :: 'png' | 'raw_bitmap' | 'eps', - Options :: [render_option()]) -> binary(). - -render(Image, Type, Options) -> - {render_engine, RenderType} = proplists:lookup(render_engine, Options), - call(Image, {render, Type, RenderType}). - - -%% @spec information(egd_image()) -> ok -%% @hidden -%% @doc Writes out information about the image. This is a debug feature -%% mainly. - -information(Pid) -> - cast(Pid, information). - -%% @spec line(egd_image(), point(), point(), color()) -> ok -%% @doc Creates a line object from P1 to P2 in the image. - --spec line( - Image :: egd_image(), - P1 :: point(), - P2 :: point(), - Color :: color()) -> 'ok'. - -line(Image, P1, P2, Color) -> - cast(Image, {line, P1, P2, Color}). - -%% @spec color( Value | Name ) -> color() -%% where -%% Value = {byte(), byte(), byte()} | {byte(), byte(), byte(), byte()} -%% Name = black | silver | gray | white | maroon | red | purple | fuchia | green | lime | olive | yellow | navy | blue | teal | aqua -%% @doc Creates a color reference. - --spec color(Value :: {byte(), byte(), byte()} | {byte(), byte(), byte(), byte()} | atom()) -> - color(). - -color(Color) -> - egd_primitives:color(Color). - -%% @spec color(egd_image(), {byte(), byte(), byte()}) -> color() -%% @doc Creates a color reference. -%% @hidden - -color(_Image, Color) -> - egd_primitives:color(Color). - -%% @spec text(egd_image(), point(), font(), string(), color()) -> ok -%% @doc Creates a text object. - -text(Image, P, Font, Text, Color) -> - cast(Image, {text, P, Font, Text, Color}). - -%% @spec rectangle(egd_image(), point(), point(), color()) -> ok -%% @doc Creates a rectangle object. - -rectangle(Image, P1, P2, Color) -> - cast(Image, {rectangle, P1, P2, Color}). - -%% @spec filledRectangle(egd_image(), point(), point(), color()) -> ok -%% @doc Creates a filled rectangle object. - -filledRectangle(Image, P1, P2, Color) -> - cast(Image, {filled_rectangle, P1, P2, Color}). - -%% @spec filledEllipse(egd_image(), point(), point(), color()) -> ok -%% @doc Creates a filled ellipse object. - -filledEllipse(Image, P1, P2, Color) -> - cast(Image, {filled_ellipse, P1, P2, Color}). - -%% @spec filledTriangle(egd_image(), point(), point(), point(), color()) -> ok -%% @hidden -%% @doc Creates a filled triangle object. - -filledTriangle(Image, P1, P2, P3, Color) -> - cast(Image, {filled_triangle, P1, P2, P3, Color}). - -%% @spec polygon(egd_image(), [point()], color()) -> ok -%% @hidden -%% @doc Creates a filled filled polygon object. - -polygon(Image, Pts, Color) -> - cast(Image, {polygon, Pts, Color}). - -%% @spec arc(egd_image(), point(), point(), color()) -> ok -%% @hidden -%% @doc Creates an arc with radius of bbx corner. - -arc(Image, P1, P2, Color) -> - cast(Image, {arc, P1, P2, Color}). - -%% @spec arc(egd_image(), point(), point(), integer(), color()) -> ok -%% @hidden -%% @doc Creates an arc. - -arc(Image, P1, P2, D, Color) -> - cast(Image, {arc, P1, P2, D, Color}). - -%% @spec save(binary(), string()) -> ok -%% @doc Saves the binary to file. - -save(Binary, Filename) when is_binary(Binary) -> - ok = file:write_file(Filename, Binary), - ok. -% --------------------------------- -% Aux functions -% --------------------------------- - -cast(Pid, Command) -> - Pid ! {egd, self(), Command}, - ok. - -call(Pid, Command) -> - Pid ! {egd, self(), Command}, - receive {egd, Pid, Result} -> Result end. - -% --------------------------------- -% Server loop -% --------------------------------- - -init(W,H) -> - Image = egd_primitives:create(W,H), - loop(Image). - -loop(Image) -> - receive - % Quitting - {egd, _Pid, destroy} -> ok; - - % Rendering - {egd, Pid, {render, BinaryType, RenderType}} -> - case BinaryType of - raw_bitmap -> - Bitmap = egd_render:binary(Image, RenderType), - Pid ! {egd, self(), Bitmap}, - loop(Image); - eps -> - Eps = egd_render:eps(Image), - Pid ! {egd, self(), Eps}, - loop(Image); - png -> - Bitmap = egd_render:binary(Image, RenderType), - Png = egd_png:binary( - Image#image.width, - Image#image.height, - Bitmap), - Pid ! {egd, self(), Png}, - loop(Image); - Unhandled -> - Pid ! {egd, self(), {error, {format, Unhandled}}}, - loop(Image) - end; - - % Drawing primitives - {egd, _Pid, {line, P1, P2, C}} -> - loop(egd_primitives:line(Image, P1, P2, C)); - {egd, _Pid, {text, P, Font, Text, C}} -> - loop(egd_primitives:text(Image, P, Font, Text, C)); - {egd, _Pid, {filled_ellipse, P1, P2, C}} -> - loop(egd_primitives:filledEllipse(Image, P1, P2, C)); - {egd, _Pid, {filled_rectangle, P1, P2, C}} -> - loop(egd_primitives:filledRectangle(Image, P1, P2, C)); - {egd, _Pid, {filled_triangle, P1, P2, P3, C}} -> - loop(egd_primitives:filledTriangle(Image, P1, P2, P3, C)); - {egd, _Pid, {polygon, Pts, C}} -> - loop(egd_primitives:polygon(Image, Pts, C)); - {egd, _Pid, {arc, P1, P2, C}} -> - loop(egd_primitives:arc(Image, P1, P2, C)); - {egd, _Pid, {arc, P1, P2, D, C}} -> - loop(egd_primitives:arc(Image, P1, P2, D, C)); - {egd, _Pid, {rectangle, P1, P2, C}} -> - loop(egd_primitives:rectangle(Image, P1, P2, C)); - {egd, _Pid, information} -> - egd_primitives:info(Image), - loop(Image); - _ -> - loop(Image) - end. diff --git a/lib/percept/src/egd.hrl b/lib/percept/src/egd.hrl deleted file mode 100644 index fc0a7e10ee..0000000000 --- a/lib/percept/src/egd.hrl +++ /dev/null @@ -1,45 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - --type rgba_float() :: {float(), float(), float(), float()}. --type rgba_byte() :: {byte(), byte(), byte(), byte()}. --type rgb() :: {byte(), byte(), byte()}. - --record(image_object, { - type, - points = [], - span, - internals, - intervals, - color}). % RGBA in float values - --record(image, { - width, - height, - objects = [], - background = {1.0,1.0,1.0,1.0}, - image}). - --define(debug, void). - --ifdef(debug). --define(dbg(X), io:format("DEBUG: ~p:~p~n",[?MODULE, X])). --else. --define(dbg(X), ok). --endif. diff --git a/lib/percept/src/egd_font.erl b/lib/percept/src/egd_font.erl deleted file mode 100644 index ef1cc434df..0000000000 --- a/lib/percept/src/egd_font.erl +++ /dev/null @@ -1,173 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - -%% -%% @doc egd_font -%% - --module(egd_font). - --export([load/1, size/1, glyph/2]). --include("egd.hrl"). - -%% Font represenatation in ets table -%% egd_font_table -%% -%% Information: -%% {Key, Description, Size} -%% Key :: {Font :: atom(), information} -%% Description :: any(), Description header from font file -%% Size :: {W :: integer(), H :: integer()} -%% -%% Glyphs: -%% {Key, Translation LSs} where -%% Key :: {Font :: atom(), Code :: integer()}, Code = glyph char code -%% Translation :: { -%% W :: integer(), % BBx width -%% H :: integer(), % BBx height -%% X0 :: integer(), % X start -%% Y0 :: integer(), % Y start -%% Xm :: integer(), % Glyph X move when drawing -%% } -%% LSs :: [[{Xl :: integer(), Xr :: integer()}]] -%% The first list is height (top to bottom), the inner list is the list -%% of line spans for the glyphs horizontal pixels. -%% - -%%========================================================================== -%% Interface functions -%%========================================================================== - -size(Font) -> - [{_Key, _Description, Size}] = ets:lookup(egd_font_table,{Font,information}), - Size. - -glyph(Font, Code) -> - [{_Key, Translation, LSs}] = ets:lookup(egd_font_table,{Font,Code}), - {Translation, LSs}. - -load(Filename) -> - {ok, Bin} = file:read_file(Filename), - Font = erlang:binary_to_term(Bin), - load_font_header(Font). - -%%========================================================================== -%% Internal functions -%%========================================================================== - -%% ETS handler functions - -initialize_table() -> - egd_font_table = ets:new(egd_font_table, [named_table, ordered_set, public]), - ok. - -glyph_insert(Font, Code, Translation, LSs) -> - Element = {{Font, Code}, Translation, LSs}, - ets:insert(egd_font_table, Element). - -font_insert(Font, Description, Dimensions) -> - Element = {{Font, information}, Description, Dimensions}, - ets:insert(egd_font_table, Element). - -%% Font loader functions - -is_font_loaded(Font) -> - try - case ets:lookup(egd_font_table, {Font, information}) of - [] -> false; - _ -> true - end - catch - error:_ -> - initialize_table(), - false - end. - - -load_font_header({_Type, _Version, Font}) -> - load_font_body(Font). - -load_font_body({Key,Desc,W,H,Glyphs,Bitmaps}) -> - case is_font_loaded(Key) of - true -> Key; - false -> - % insert dimensions - font_insert(Key, Desc, {W,H}), - parse_glyphs(Glyphs, Bitmaps, Key), - Key - end. - -parse_glyphs([], _ , _Key) -> ok; -parse_glyphs([Glyph|Glyphs], Bs, Key) -> - {Code, Translation, LSs} = parse_glyph(Glyph, Bs), - glyph_insert(Key, Code, Translation, LSs), - parse_glyphs(Glyphs, Bs, Key). - -parse_glyph({Code,W,H,X0,Y0,Xm,Offset}, Bitmasks) -> - BytesPerLine = ((W+7) div 8), - NumBytes = BytesPerLine*H, - <<_:Offset/binary,Bitmask:NumBytes/binary,_/binary>> = Bitmasks, - LSs = render_glyph(W,H,X0,Y0,Xm,Bitmask), - {Code, {W,H,X0,Y0,Xm}, LSs}. - -render_glyph(W, H, X0, Y0, Xm, Bitmask) -> - render_glyph(W,{0,H},X0,Y0,Xm,Bitmask, []). -render_glyph(_W, {H,H}, _X0, _Y0, _Xm, _Bitmask, Out) -> Out; -render_glyph(W, {Hi,H}, X0, Y0,Xm, Bitmask , LSs) -> - N = ((W+7) div 8), - O = N*Hi, - <<_:O/binary, Submask/binary>> = Bitmask, - LS = render_glyph_horizontal( - Submask, % line glyph bitmask - {down, W - 1}, % loop state - W - 1, % Width - []), % Linespans - render_glyph(W,{Hi+1,H},X0,Y0,Xm, Bitmask, [LS|LSs]). - -render_glyph_horizontal(Value, {Pr, Px}, 0, Spans) -> - Cr = bit_spin(Value, 0), - case {Pr,Cr} of - {up , up } -> % closure of interval since its last - [{0, Px}|Spans]; - {up , down} -> % closure of interval - [{1, Px}|Spans]; - {down, up } -> % beginning of interval - [{0, 0}|Spans]; - {down, down} -> % no change in interval - Spans - end; -render_glyph_horizontal(Value, {Pr, Px}, Cx, Spans) -> - Cr = bit_spin(Value, Cx), - case {Pr,Cr} of - {up , up } -> % no change in interval - render_glyph_horizontal(Value, {Cr, Px}, Cx - 1, Spans); - {up , down} -> % closure of interval - render_glyph_horizontal(Value, {Cr, Cx}, Cx - 1, [{Cx+1,Px}|Spans]); - {down, up } -> % beginning of interval - render_glyph_horizontal(Value, {Cr, Cx}, Cx - 1, Spans); - {down, down} -> % no change in interval - render_glyph_horizontal(Value, {Cr, Px}, Cx - 1, Spans) - end. - -bit_spin(Value, Cx) -> - <<_:Cx, Bit:1, _/bits>> = Value, - case Bit of - 1 -> up; - 0 -> down - end. diff --git a/lib/percept/src/egd_png.erl b/lib/percept/src/egd_png.erl deleted file mode 100644 index fe660513b4..0000000000 --- a/lib/percept/src/egd_png.erl +++ /dev/null @@ -1,105 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - - -%% This code was originally written by Dan Gudmundsson for png-handling in -%% wings3d (e3d__png). -%% -%% @doc egd -%% - --module(egd_png). - --export([binary/3]). - --include("egd.hrl"). - --define(MAGIC, 137,$P,$N,$G,$\r,$\n,26,$\n). - --define(GREYSCALE, 0). --define(TRUECOLOUR, 2). --define(INDEXED, 3). --define(GREYSCALE_A, 4). --define(TRUECOLOUR_A,6). - --define(MAX_WBITS,15). - --define(CHUNK, 240). - --define(get4p1(Idx),((Idx) bsr 4)). --define(get4p2(Idx),((Idx) band 16#0F)). --define(get2p1(Idx),((Idx) bsr 6)). --define(get2p2(Idx),(((Idx) bsr 4) band 3)). --define(get2p3(Idx),(((Idx) bsr 2) band 3)). --define(get2p4(Idx),((Idx) band 3)). --define(get1p1(Idx),((Idx) bsr 7)). --define(get1p2(Idx),(((Idx) bsr 6) band 1)). --define(get1p3(Idx),(((Idx) bsr 5) band 1)). --define(get1p4(Idx),(((Idx) bsr 4) band 1)). --define(get1p5(Idx),(((Idx) bsr 3) band 1)). --define(get1p6(Idx),(((Idx) bsr 2) band 1)). --define(get1p7(Idx),(((Idx) bsr 1) band 1)). --define(get1p8(Idx),((Idx) band 1)). - -binary(W, H, Bitmap) when is_binary(Bitmap) -> - Z = zlib:open(), - Binary = bitmap2png(W, H, Bitmap, Z), - zlib:close(Z), - Binary. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% Begin Tainted - -bitmap2png(W, H, Bitmap,Z) -> - HDR = create_chunk(<<"IHDR",W:32,H:32,8:8,(png_type(r8g8b8)):8,0:8,0:8,0:8>>,Z), - DATA = create_chunk(["IDAT",compress_image(0,3*W,Bitmap,[])],Z), - END = create_chunk(<<"IEND">>,Z), - list_to_binary([?MAGIC,HDR,DATA,END]). - -compress_image(I,RowLen, Bin, Acc) -> - Pos = I*RowLen, - case Bin of - <<_:Pos/binary,Row:RowLen/binary,_/binary>> -> - Filtered = filter_row(Row,RowLen), - compress_image(I+1,RowLen,Bin,[Filtered|Acc]); - _ when Pos == size(Bin) -> - Filtered = list_to_binary(lists:reverse(Acc)), - Compressed = zlib:compress(Filtered), - Compressed - end. - -filter_row(Row,_RowLen) -> - [0,Row]. - -% dialyzer warnings -%png_type(g8) -> ?GREYSCALE; -%png_type(a8) -> ?GREYSCALE; -%png_type(r8g8b8a8) -> ?TRUECOLOUR_A; -png_type(r8g8b8) -> ?TRUECOLOUR. - -create_chunk(Bin,Z) when is_list(Bin) -> - create_chunk(list_to_binary(Bin),Z); -create_chunk(Bin,Z) when is_binary(Bin) -> - Sz = size(Bin)-4, - Crc = zlib:crc32(Z,Bin), - <<Sz:32,Bin/binary,Crc:32>>. - -% End tainted diff --git a/lib/percept/src/egd_primitives.erl b/lib/percept/src/egd_primitives.erl deleted file mode 100644 index b64189c552..0000000000 --- a/lib/percept/src/egd_primitives.erl +++ /dev/null @@ -1,412 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - -%% -%% @doc egd_primitives -%% - - --module(egd_primitives). --export([create/2, - color/1, - pixel/3, - polygon/3, - line/4, - line/5, - arc/4, - arc/5, - rectangle/4, - filledRectangle/4, - filledEllipse/4, - filledTriangle/5, - text/5]). - --export([info/1, - object_info/1, - rgb_float2byte/1]). - --export([arc_to_edges/3, - convex_hull/1, - edges/1]). - --include("egd.hrl"). - -%% API info -info(I) -> - W = I#image.width, H = I#image.height, - io:format("Dimensions: ~p x ~p~n", [W,H]), - io:format("Number of image objects: ~p~n", [length(I#image.objects)]), - TotalPoints = info_objects(I#image.objects,0), - io:format("Total points: ~p [~p %]~n", [TotalPoints, 100*TotalPoints/(W*H)]), - ok. - -info_objects([],N) -> N; -info_objects([O | Os],N) -> - Points = length(O#image_object.points), -info_objects(Os,N+Points). - -object_info(O) -> - io:format("Object information: ~p~n", [O#image_object.type]), - io:format("- Number of points: ~p~n", [length(O#image_object.points)]), - io:format("- Bounding box: ~p~n", [O#image_object.span]), - io:format("- Color: ~p~n", [O#image_object.color]), - ok. - -%% interface functions - -line(#image{objects=Os}=I, Sp, Ep, Color) -> - line(#image{objects=Os}=I, Sp, Ep, 1, Color). - -line(#image{objects=Os}=I, Sp, Ep, Wd, Color) -> - I#image{objects=[#image_object{ - internals = Wd, - type = line, - points = [Sp, Ep], - span = span([Sp, Ep]), - color = Color}|Os]}. - -arc(I, {Sx,Sy} = Sp, {Ex,Ey} = Ep, Color) -> - X = Ex - Sx, - Y = Ey - Sy, - R = math:sqrt(X*X + Y*Y)/2, - arc(I, Sp, Ep, R, Color). - -arc(#image{objects=Os}=I, Sp, Ep, D, Color) -> - SpanPts = lists:flatten([ - [{X + D, Y + D}, - {X + D, Y - D}, - {X - D, Y + D}, - {X - D, Y - D}] || {X,Y} <- [Sp,Ep]]), - - I#image{objects=[#image_object{ - internals = D, - type = arc, - points = [Sp, Ep], - span = span(SpanPts), - color = Color}|Os]}. - -pixel(#image{objects=Os}=I, Point, Color) -> - I#image{objects=[#image_object{ - type = pixel, - points = [Point], - span = span([Point]), - color = Color}|Os]}. - -rectangle(#image{objects=Os}=I, Sp, Ep, Color) -> - I#image{objects=[#image_object{ - type = rectangle, - points = [Sp, Ep], - span = span([Sp, Ep]), - color = Color}|Os]}. - -filledRectangle(#image{objects=Os}=I, Sp, Ep, Color) -> - I#image{objects=[#image_object{ - type = filled_rectangle, - points = [Sp, Ep], - span = span([Sp, Ep]), - color = Color}|Os]}. - -filledEllipse(#image{objects=Os}=I, Sp, Ep, Color) -> - {X0,Y0,X1,Y1} = Span = span([Sp, Ep]), - Xr = (X1 - X0)/2, - Yr = (Y1 - Y0)/2, - Xp = - X0 - Xr, - Yp = - Y0 - Yr, - I#image{objects=[#image_object{ - internals = {Xp,Yp, Xr*Xr,Yr*Yr}, - type = filled_ellipse, - points = [Sp, Ep], - span = Span, - color = Color}|Os]}. - -filledTriangle(#image{objects=Os}=I, P1, P2, P3, Color) -> - I#image{objects=[#image_object{ - type = filled_triangle, - points = [P1,P2,P3], - span = span([P1,P2,P3]), - color = Color}|Os]}. - -polygon(#image{objects=Os}=I, Points, Color) -> - I#image{objects=[#image_object{ - type = polygon, - points = Points, - span = span(Points), - color = Color}|Os]}. - -text(#image{objects=Os}=I, {Xs,Ys}=Sp, Font, Text, Color) -> - {FW,FH} = egd_font:size(Font), - Length = length(Text), - Ep = {Xs + Length*FW, Ys + FH + 5}, - I#image{objects=[#image_object{ - internals = {Font, Text}, - type = text_horizontal, - points = [Sp], - span = span([Sp,Ep]), - color = Color}|Os]}. - -create(W, H) -> - #image{width = W, height = H}. - -color(Color) when is_atom(Color) -> rgba_byte2float(name_to_color(Color, 255)); -color({Color, A}) when is_atom(Color) -> rgba_byte2float(name_to_color(Color, A)); -color({R,G,B}) -> rgba_byte2float({R,G,B, 255}); -color(C) -> rgba_byte2float(C). - -name_to_color(Color, A) -> - case Color of - %% HTML default colors - black -> { 0, 0, 0, A}; - silver -> {192, 192, 192, A}; - gray -> {128, 128, 128, A}; - white -> {128, 0, 0, A}; - maroon -> {255, 0, 0, A}; - red -> {128, 0, 128, A}; - purple -> {128, 0, 128, A}; - fuchia -> {255, 0, 255, A}; - green -> { 0, 128, 0, A}; - lime -> { 0, 255, 0, A}; - olive -> {128, 128, 0, A}; - yellow -> {255, 255, 0, A}; - navy -> { 0, 0, 128, A}; - blue -> { 0, 0, 255, A}; - teal -> { 0, 128, 0, A}; - aqua -> { 0, 255, 155, A}; - - %% HTML color extensions - steelblue -> { 70, 130, 180, A}; - royalblue -> { 4, 22, 144, A}; - cornflowerblue -> {100, 149, 237, A}; - lightsteelblue -> {176, 196, 222, A}; - mediumslateblue -> {123, 104, 238, A}; - slateblue -> {106, 90, 205, A}; - darkslateblue -> { 72, 61, 139, A}; - midnightblue -> { 25, 25, 112, A}; - darkblue -> { 0, 0, 139, A}; - mediumblue -> { 0, 0, 205, A}; - dodgerblue -> { 30, 144, 255, A}; - deepskyblue -> { 0, 191, 255, A}; - lightskyblue -> {135, 206, 250, A}; - skyblue -> {135, 206, 235, A}; - lightblue -> {173, 216, 230, A}; - powderblue -> {176, 224, 230, A}; - azure -> {240, 255, 255, A}; - lightcyan -> {224, 255, 255, A}; - paleturquoise -> {175, 238, 238, A}; - mediumturquoise -> { 72, 209, 204, A}; - lightseagreen -> { 32, 178, 170, A}; - darkcyan -> { 0, 139, 139, A}; - cadetblue -> { 95, 158, 160, A}; - darkturquoise -> { 0, 206, 209, A}; - cyan -> { 0, 255, 255, A}; - turquoise -> { 64, 224, 208, A}; - aquamarine -> {127, 255, 212, A}; - mediumaquamarine -> {102, 205, 170, A}; - darkseagreen -> {143, 188, 143, A}; - mediumseagreen -> { 60, 179, 113, A}; - seagreen -> { 46, 139, 87, A}; - darkgreen -> { 0, 100, 0, A}; - forestgreen -> { 34, 139, 34, A}; - limegreen -> { 50, 205, 50, A}; - chartreuse -> {127, 255, 0, A}; - lawngreen -> {124, 252, 0, A}; - greenyellow -> {173, 255, 47, A}; - yellowgreen -> {154, 205, 50, A}; - palegreen -> {152, 251, 152, A}; - lightgreen -> {144, 238, 144, A}; - springgreen -> { 0, 255, 127, A}; - darkolivegreen -> { 85, 107, 47, A}; - olivedrab -> {107, 142, 35, A}; - darkkhaki -> {189, 183, 107, A}; - darkgoldenrod -> {184, 134, 11, A}; - goldenrod -> {218, 165, 32, A}; - gold -> {255, 215, 0, A}; - khaki -> {240, 230, 140, A}; - palegoldenrod -> {238, 232, 170, A}; - blanchedalmond -> {255, 235, 205, A}; - moccasin -> {255, 228, 181, A}; - wheat -> {245, 222, 179, A}; - navajowhite -> {255, 222, 173, A}; - burlywood -> {222, 184, 135, A}; - tan -> {210, 180, 140, A}; - rosybrown -> {188, 143, 143, A}; - sienna -> {160, 82, 45, A}; - saddlebrown -> {139, 69, 19, A}; - chocolate -> {210, 105, 30, A}; - peru -> {205, 133, 63, A}; - sandybrown -> {244, 164, 96, A}; - darkred -> {139, 0, 0, A}; - brown -> {165, 42, 42, A}; - firebrick -> {178, 34, 34, A}; - indianred -> {205, 92, 92, A}; - lightcoral -> {240, 128, 128, A}; - salmon -> {250, 128, 114, A}; - darksalmon -> {233, 150, 122, A}; - lightsalmon -> {255, 160, 122, A}; - coral -> {255, 127, 80, A}; - tomato -> {255, 99, 71, A}; - darkorange -> {255, 140, 0, A}; - orange -> {255, 165, 0, A}; - orangered -> {255, 69, 0, A}; - crimson -> {220, 20, 60, A}; - deeppink -> {255, 20, 147, A}; - fuchsia -> {255, 0, 255, A}; - magenta -> {255, 0, 255, A}; - hotpink -> {255, 105, 180, A}; - lightpink -> {255, 182, 193, A}; - pink -> {255, 192, 203, A}; - palevioletred -> {219, 112, 147, A}; - mediumvioletred -> {199, 21, 133, A}; - darkmagenta -> {139, 0, 139, A}; - mediumpurple -> {147, 112, 219, A}; - blueviolet -> {138, 43, 226, A}; - indigo -> { 75, 0, 130, A}; - darkviolet -> {148, 0, 211, A}; - darkorchid -> {153, 50, 204, A}; - mediumorchid -> {186, 85, 211, A}; - orchid -> {218, 112, 214, A}; - violet -> {238, 130, 238, A}; - plum -> {221, 160, 221, A}; - thistle -> {216, 191, 216, A}; - lavender -> {230, 230, 250, A}; - ghostwhite -> {248, 248, 255, A}; - aliceblue -> {240, 248, 255, A}; - mintcream -> {245, 255, 250, A}; - honeydew -> {240, 255, 240, A}; - lemonchiffon -> {255, 250, 205, A}; - cornsilk -> {255, 248, 220, A}; - lightyellow -> {255, 255, 224, A}; - ivory -> {255, 255, 240, A}; - floralwhite -> {255, 250, 240, A}; - linen -> {250, 240, 230, A}; - oldlace -> {253, 245, 230, A}; - antiquewhite -> {250, 235, 215, A}; - bisque -> {255, 228, 196, A}; - peachpuff -> {255, 218, 185, A}; - papayawhip -> {255, 239, 213, A}; - beige -> {245, 245, 220, A}; - seashell -> {255, 245, 238, A}; - lavenderblush -> {255, 240, 245, A}; - mistyrose -> {255, 228, 225, A}; - snow -> {255, 250, 250, A}; - whitesmoke -> {245, 245, 245, A}; - gainsboro -> {220, 220, 220, A}; - lightgrey -> {211, 211, 211, A}; - darkgray -> {169, 169, 169, A}; - lightslategray -> {119, 136, 153, A}; - slategray -> {112, 128, 144, A}; - dimgray -> {105, 105, 105, A}; - darkslategray -> { 47, 79, 79, A}; - mediumspringgreen -> { 0, 250, 154, A}; - lightgoldenrodyellow -> {250, 250, 210, A} - end. - - -%%% Generic transformations - -%% arc_to_edges -%% In: -%% P1 :: point(), -%% P2 :: point(), -%% D :: float(), -%% Out: -%% Res :: [edges()] - -arc_to_edges(P0, P1, D) when abs(D) < 0.5 -> [{P0,P1}]; -arc_to_edges({X0,Y0}, {X1,Y1}, D) -> - Vx = X1 - X0, - Vy = Y1 - Y0, - - Mx = X0 + 0.5 * Vx, - My = Y0 + 0.5 * Vy, - - % Scale V by Rs - L = math:sqrt(Vx*Vx + Vy*Vy), - Sx = D*Vx/L, - Sy = D*Vy/L, - - Bx = trunc(Mx - Sy), - By = trunc(My + Sx), - - arc_to_edges({X0,Y0}, {Bx,By}, D/4) ++ arc_to_edges({Bx,By}, {X1,Y1}, D/4). - -%% edges -%% In: -%% Pts :: [point()] -%% Out: -%% Edges :: [{point(),point()}] - -edges([]) -> []; -edges([P0|_] = Pts) -> edges(Pts, P0,[]). -edges([P1], P0, Out) -> [{P1,P0}|Out]; -edges([P1,P2|Pts],P0,Out) -> edges([P2|Pts],P0,[{P1,P2}|Out]). - -%% convex_hull -%% In: -%% Ps :: [point()] -%% Out: -%% Res :: [point()] - -convex_hull(Ps) -> - P0 = lower_right(Ps), - [P1|Ps1] = lists:sort(fun - (P2,P1) -> - case point_side({P1,P0},P2) of - left -> true; - _ -> false - end - end, Ps -- [P0]), - convex_hull(Ps1, [P1,P0]). - -convex_hull([], W) -> W; -convex_hull([P|Pts], [P1,P2|W]) -> - case point_side({P2,P1},P) of - left -> convex_hull(Pts, [P,P1,P2|W]); - _ -> convex_hull([P|Pts], [P2|W]) - end. - -lower_right([P|Pts]) -> lower_right(P, Pts). -lower_right(P, []) -> P; -lower_right({X0,Y0}, [{_,Y}|Pts]) when Y < Y0 -> lower_right({X0,Y0}, Pts); -lower_right({X0,Y0}, [{X,Y}|Pts]) when X < X0, Y < Y0 -> lower_right({X0,Y0}, Pts); -lower_right(_,[P|Pts]) -> lower_right(P, Pts). - -point_side({{X0,Y0}, {X1, Y1}}, {X2, Y2}) -> point_side((X1 - X0)*(Y2 - Y0) - (X2 - X0)*(Y1 - Y0)). -point_side(D) when D > 0 -> left; -point_side(D) when D < 0 -> right; -point_side(_) -> on_line. - -%% AUX - -span([{X0,Y0}|Points]) -> - span(Points,X0,Y0,X0,Y0). -span([{X0,Y0}|Points],Xmin,Ymin,Xmax,Ymax) -> - span(Points,erlang:min(Xmin,X0), - erlang:min(Ymin,Y0), - erlang:max(Xmax,X0), - erlang:max(Ymax,Y0)); -span([],Xmin,Ymin,Xmax,Ymax) -> - {Xmin,Ymin,Xmax,Ymax}. - - -rgb_float2byte({R,G,B}) -> rgb_float2byte({R,G,B,1.0}); -rgb_float2byte({R,G,B,A}) -> - {trunc(R*255), trunc(G*255), trunc(B*255), trunc(A*255)}. - -rgba_byte2float({R,G,B,A}) -> - {R/255,G/255,B/255,A/255}. diff --git a/lib/percept/src/egd_render.erl b/lib/percept/src/egd_render.erl deleted file mode 100644 index 6c708e3e86..0000000000 --- a/lib/percept/src/egd_render.erl +++ /dev/null @@ -1,664 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - -%% -%% @doc egd_render -%% - --module(egd_render). - --export([binary/1, binary/2]). --export([eps/1]). --compile(inline). - --export([line_to_linespans/3]). - --include("egd.hrl"). --define('DummyC',0). - -binary(Image) -> - binary(Image, opaque). - -binary(Image, Type) -> - parallel_binary(precompile(Image),Type). - -parallel_binary(Image = #image{ height = Height },Type) -> - case erlang:min(erlang:system_info(schedulers), Height) of - 1 -> - % if the height or the number of schedulers is 1 - % do the scanlines in this process. - W = Image#image.width, - Bg = Image#image.background, - Os = Image#image.objects, - erlang:list_to_binary([scanline(Y, Os, {0,0,W - 1, Bg}, Type) - || Y <- lists:seq(1, Height)]); - Np -> - Pids = start_workers(Np, Type), - Handler = handle_workers(Height, Pids), - init_workers(Image, Handler, Pids), - Res = receive_binaries(Height), - finish_workers(Pids), - Res - end. - -start_workers(Np, Type) -> - start_workers(Np, Type, []). - -start_workers( 0, _, Pids) -> Pids; -start_workers(Np, Type, Pids) when Np > 0 -> - start_workers(Np - 1, Type, [spawn_link(fun() -> worker(Type) end)|Pids]). - -worker(Type) -> - receive - {Pid, data, #image{ objects = Os, width = W, background = Bg }} -> - worker(Os, W, Bg, Type, Pid) - end. - -worker(Objects, Width, Bg, Type, Collector) -> - receive - {Pid, scan, {Ys, Ye}} -> - lists:foreach(fun - (Y) -> - Bin = erlang:list_to_binary(scanline(Y, Objects, {0,0,Width - 1, Bg}, Type)), - Collector ! {scan, Y, Bin} - end, lists:seq(Ys,Ye)), - Pid ! {self(), scan_complete}, - worker(Objects, Width, Bg, Type, Collector); - {Pid, scan, Y} -> - Bin = erlang:list_to_binary(scanline(Y, Objects, {0,0,Width - 1, Bg}, Type)), - Collector ! {scan, Y, Bin}, - Pid ! {self(), scan_complete}, - worker(Objects, Width, Bg, Type, Collector); - {_, done} -> - ok - end. - -init_workers(_Image, _Handler, []) -> ok; -init_workers(Image, Handler, [Pid|Pids]) -> - Pid ! {self(), data, Image}, - Handler ! {Pid, scan_complete}, - init_workers(Image, Handler, Pids). - -handle_workers(H, Pids) -> - spawn_link(fun() -> handle_workers(H, H, length(Pids)) end). - -handle_workers(_, 0, _) -> ok; -handle_workers(H, Hi, Np) when H > 0 -> - N = trunc(Hi/(2*Np)), - receive - {Pid, scan_complete} -> - if N < 2 -> - Pid ! {self(), scan, Hi}, - handle_workers(H, Hi - 1, Np); - true -> - Pid ! {self(), scan, {Hi - N, Hi}}, - handle_workers(H, Hi - 1 - N, Np) - end - end. - -finish_workers([]) -> ok; -finish_workers([Pid|Pids]) -> - Pid ! {self(), done}, - finish_workers(Pids). - -receive_binaries(H) -> - receive_binaries(H, []). - -receive_binaries(0, Bins) -> erlang:list_to_binary(Bins); -receive_binaries(H, Bins) when H > 0 -> - receive - {scan, H, Bin} -> - receive_binaries(H - 1, [Bin|Bins]) - end. - -scanline(Y, Os, {_,_,Width,_}=LSB, Type) -> - OLSs = parse_objects_on_line(Y-1, Width, Os), - RLSs = resulting_line_spans([LSB|OLSs],Type), - [ lists:duplicate(Xr - Xl + 1, <<(trunc(R*255)):8,(trunc(G*255)):8,(trunc(B*255)):8>>) || {_,Xl, Xr, {R,G,B,_}} <- RLSs ]. - -resulting_line_spans(LSs,Type) -> - %% Build a list of "transitions" from left to right. - Trans = line_spans_to_trans(LSs), - %% Convert list of "transitions" to linespans. - trans_to_line_spans(Trans,Type). - -line_spans_to_trans(LSs) -> - line_spans_to_trans(LSs,[],0). - -line_spans_to_trans([],Db,_) -> - lists:sort(Db); -line_spans_to_trans([{_,L,R,C}|LSs],Db,Z) -> - line_spans_to_trans(LSs,[{{L,Z,start},C},{{R+1,Z,stop},C}|Db],Z+1). - -trans_to_line_spans(Trans,Type) -> - trans_to_line_spans(simplify_trans(Trans,Type,[],{0.0,0.0,0.0,0.0},[])). - -trans_to_line_spans(SimpleTrans) -> - trans_to_line_spans1(SimpleTrans,[]). - -trans_to_line_spans1([],Spans) -> - Spans; -trans_to_line_spans1([_],Spans) -> - Spans; -trans_to_line_spans1([{L1,_},{L2,C2}|SimpleTrans],Spans) -> - %% We are going backwards now... - trans_to_line_spans1([{L2,C2}|SimpleTrans],[{?DummyC,L2,L1-1,C2}|Spans]). - -simplify_trans([],_,_,_,Acc) -> - Acc; -simplify_trans([{{L,_,_},_}|_] = Trans,Type,Layers,OldC,Acc) -> - {NextTrans,RestTrans} = - lists:splitwith(fun({{L1,_,_},_}) when L1 == L -> - true; - (_) -> - false - end, Trans), - {C,NewLayers} = color(NextTrans,Layers,Type,OldC), - case OldC of - C -> %% No change in color, so transition unnecessary. - simplify_trans(RestTrans,Type,NewLayers,OldC,Acc); - _ -> - simplify_trans(RestTrans,Type,NewLayers,C,[{L,C}|Acc]) - end. - -color(Trans,Layers,Type,OldC) -> - case modify_layers(Layers,Trans) of - Layers -> - {OldC,Layers}; - NewLayers -> - {color(NewLayers,Type),NewLayers} - end. - -color([],_) -> {0.0,0.0,0.0,0.0}; -color([{_,C}|_],opaque) -> C; -color(Layers,alpha) -> color1({0.0,0.0,0.0,0.0},Layers). - -color1(Color,[]) -> Color; -color1(Color,[{_,C}|Layers]) -> color1(alpha_blend(Color,C),Layers). - -modify_layers(Layers,[]) -> Layers; -modify_layers(Layers,[{{_,Z,start},C}|Trans]) -> - modify_layers(add_layer(Layers, Z, C), Trans); -modify_layers(Layers,[{{_,Z,stop },C}|Trans]) -> - modify_layers(remove_layer(Layers, Z, C), Trans). - -add_layer([{Z1,_}=H|Layers],Z,C) when Z1 > Z -> - [H|add_layer(Layers,Z,C)]; -add_layer(Layers,Z,C) -> - [{Z,C}|Layers]. - -remove_layer(Layers,Z,C) -> - Layers -- [{Z,C}]. - -alpha_blend({R1,G1,B1,A1}, {R2,G2,B2,A2}) when is_float(A1), is_float(A2)-> - Beta = A2*(1.0 - A1), - A = A1 + Beta, - R = R1*A1 + R2*Beta, - G = G1*A1 + G2*Beta, - B = B1*A1 + B2*Beta, - {R,G,B,A}. - -parse_objects_on_line(Y, Width, Objects) -> - parse_objects_on_line(Y, 1, Width, Objects, []). -parse_objects_on_line(_Y, _Z, _, [], Out) -> lists:flatten(Out); -parse_objects_on_line(Y, Z, Width, [O|Os], Out) -> - case is_object_on_line(O, Y) of - false -> - parse_objects_on_line(Y, Z + 1, Width, Os, Out); - true -> - OLs = object_line_data(O,Y,Z), - TOLs = trim_object_line_data(OLs, Width), - parse_objects_on_line(Y, Z + 1, Width, Os, [TOLs|Out]) - end. - -trim_object_line_data(OLs, Width) -> - trim_object_line_data(OLs, Width, []). -trim_object_line_data([], _, Out) -> Out; - -trim_object_line_data([{_, Xl, _, _}|OLs], Width, Out) when Xl > Width -> - trim_object_line_data(OLs, Width, Out); -trim_object_line_data([{_, _, Xr, _}|OLs], Width, Out) when Xr < 0 -> - trim_object_line_data(OLs, Width, Out); -trim_object_line_data([{Z, Xl, Xr, C}|OLs], Width, Out) -> - trim_object_line_data(OLs, Width, [{Z, erlang:max(0,Xl), erlang:min(Xr,Width), C}|Out]). - -% object_line_data -% In: -% Object :: image_object() -% Y :: index of height -% Z :: index of depth -% Out: -% OLs = [{Z, Xl, Xr, Color}] -% Z = index of height -% Xl = left X index -% Xr = right X index -% Purpose: -% Calculate the length (start and finish index) of an objects horizontal -% line given the height index. - -object_line_data(#image_object{type=rectangle, - span={X0,Y0,X1,Y1}, color=C}, Y, Z) -> - if - Y0 =:= Y ; Y1 =:= Y -> - [{Z, X0, X1, C}]; - true -> - [{Z, X0, X0, C}, - {Z, X1, X1, C}] - end; - -object_line_data(#image_object{type=filled_rectangle, - span={X0, _, X1, _}, color=C}, _Y, Z) -> - [{Z, X0, X1, C}]; - -object_line_data(#image_object{type=filled_ellipse, - internals={Xr,Yr,Yr2}, span={X0,Y0,X1,Y1}, color=C}, Y, Z) -> - if - X1 - X0 =:= 0; Y1 - Y0 =:= 0 -> - [{Z, X0, X1, C}]; - true -> - Yo = trunc(Y - Y0 - Yr), - Yo2 = Yo*Yo, - Xo = math:sqrt((1 - Yo2/Yr2))*Xr, - [{Z, round(X0 - Xo + Xr), round(X0 + Xo + Xr), C}] - end; - -object_line_data(#image_object{type=filled_triangle, - intervals=Is, color=C}, Y, Z) -> - case lists:keyfind(Y, 1, Is) of - {Y, Xl, Xr} -> [{Z, Xl, Xr, C}]; - false -> [] - end; - -object_line_data(#image_object{type=line, - intervals=M, color={R,G,B,_}}, Y, Z) -> - case M of - #{Y := Ls} -> [{Z, Xl, Xr, {R,G,B,1.0-C/255}}||{Xl,Xr,C} <- Ls]; - _ -> [] - end; - -object_line_data(#image_object{type=polygon, - color=C, intervals=Is}, Y, Z) -> - [{Z, Xl, Xr, C} || {Yp, Xl, Xr} <- Is, Yp =:= Y]; - -object_line_data(#image_object{type=text_horizontal, - color=C, intervals=Is}, Y, Z) -> - [{Z, Xl, Xr, C} || {Yg, Xl, Xr} <- Is, Yg =:= Y]; - -object_line_data(#image_object{type=pixel, - span={X0,_,X1,_}, color=C}, _, Z) -> - [{Z, X0, X1, C}]. - -is_object_on_line(#image_object{span={_,Y0,_,Y1}}, Y) -> - if Y < Y0; Y > Y1 -> false; - true -> true - end. - -%%% primitives to line_spans - -%% compile objects to linespans - -precompile(#image{objects = Os}=I) -> - I#image{objects = precompile_objects(Os)}. - -precompile_objects([]) -> []; -precompile_objects([#image_object{type=line, internals=W, points=[P0,P1]}=O|Os]) -> - [O#image_object{intervals = linespans_to_map(line_to_linespans(P0,P1,W))}|precompile_objects(Os)]; -precompile_objects([#image_object{type=filled_triangle, points=[P0,P1,P2]}=O|Os]) -> - [O#image_object{intervals = triangle_ls(P0,P1,P2)}|precompile_objects(Os)]; -precompile_objects([#image_object{type=polygon, points=Pts}=O|Os]) -> - [O#image_object{intervals = polygon_ls(Pts)}|precompile_objects(Os)]; -precompile_objects([#image_object{type=filled_ellipse, span={X0,Y0,X1,Y1}}=O|Os]) -> - Xr = (X1 - X0)/2, - Yr = (Y1 - Y0)/2, - Yr2 = Yr*Yr, - [O#image_object{internals={Xr,Yr,Yr2}}|precompile_objects(Os)]; -precompile_objects([#image_object{type=arc, points=[P0,P1], internals=D}=O|Os]) -> - Es = egd_primitives:arc_to_edges(P0, P1, D), - Ls = lists:foldl(fun ({Ep0,Ep1},M) -> - linespans_to_map(line_to_linespans(Ep0,Ep1,1),M) - end, #{}, Es), - [O#image_object{type=line, intervals=Ls}|precompile_objects(Os)]; -precompile_objects([#image_object{type=text_horizontal, - points=[P0], internals={Font,Text}}=O|Os]) -> - [O#image_object{intervals=text_horizontal_ls(P0,Font,Text)}|precompile_objects(Os)]; -precompile_objects([O|Os]) -> - [O|precompile_objects(Os)]. - -% triangle - -triangle_ls(P1,P2,P3) -> - % Find top point (or left most top point), - % From that point, two lines will be drawn to the - % other points. - % For each Y step, - % bresenham_line_interval for each of the two lines - % Find the left most and the right most for those lines - % At an end point, a new line to the point already being drawn - % repeat same procedure as above - [Sp1, Sp2, Sp3] = tri_pt_ysort([P1,P2,P3]), - triangle_ls_lp(tri_ls_ysort(line_to_linespans(Sp1,Sp2,1)), Sp2, - tri_ls_ysort(line_to_linespans(Sp1,Sp3,1)), Sp3, []). - -% There will be Y mismatches between the two lists since bresenham is not perfect. -% I can be remedied with checking intervals this could however be costly and -% it may not be necessary, depending on how exact we need the points to be. -% It should at most differ by one and endpoints should be fine. - -triangle_ls_lp([],_,[],_,Out) -> Out; -triangle_ls_lp(LSs1, P1, [], P2, Out) -> - SLSs = tri_ls_ysort(line_to_linespans(P2,P1,1)), - N2 = length(SLSs), - N1 = length(LSs1), - if - N1 > N2 -> - [_|ILSs] = LSs1, - triangle_ls_lp(ILSs, SLSs, Out); - N2 > N1 -> - [_|ILSs] = SLSs, - triangle_ls_lp(LSs1, ILSs, Out); - true -> - triangle_ls_lp(LSs1, SLSs, Out) - end; -triangle_ls_lp([], P1, LSs2, P2, Out) -> - SLSs = tri_ls_ysort(line_to_linespans(P1,P2,1)), - N1 = length(SLSs), - N2 = length(LSs2), - if - N1 > N2 -> - [_|ILSs] = SLSs, - triangle_ls_lp(ILSs, LSs2, Out); - N2 > N1 -> - [_|ILSs] = LSs2, - triangle_ls_lp(SLSs, ILSs, Out); - true -> - triangle_ls_lp(SLSs, LSs2, Out) - end; -triangle_ls_lp([LS1|LSs1],P1,[LS2|LSs2],P2, Out) -> - {Y, Xl1, Xr1,_Ca1} = LS1, - {_, Xl2, Xr2,_Ca2} = LS2, - Xr = lists:max([Xl1,Xr1,Xl2,Xr2]), - Xl = lists:min([Xl1,Xr1,Xl2,Xr2]), - triangle_ls_lp(LSs1,P1,LSs2,P2,[{Y,Xl,Xr}|Out]). - -triangle_ls_lp([],[],Out) -> Out; -triangle_ls_lp([],_,Out) -> Out; -triangle_ls_lp(_,[],Out) -> Out; -triangle_ls_lp([LS1|LSs1], [LS2|LSs2], Out) -> - {Y, Xl1, Xr1, _Ca1} = LS1, - {_, Xl2, Xr2, _Ca2} = LS2, - Xr = lists:max([Xl1,Xr1,Xl2,Xr2]), - Xl = lists:min([Xl1,Xr1,Xl2,Xr2]), - triangle_ls_lp(LSs1,LSs2,[{Y,Xl,Xr}|Out]). - -tri_pt_ysort(Pts) -> - % {X,Y} - lists:sort( - fun ({_,Y1},{_,Y2}) -> - if Y1 > Y2 -> false; true -> true end - end, Pts). - -tri_ls_ysort(LSs) -> - % {Y, Xl, Xr, Ca} - lists:sort( - fun ({Y1,_,_,_},{Y2,_,_,_}) -> - if Y1 > Y2 -> false; true -> true end - end, LSs). - -% polygon_ls -% In: -% Pts :: [{X,Y}] -% Out: -% LSs :: [{Y,Xl,Xr}] -% Purpose: -% Make polygon line spans -% Algorithm: -% 1. Find the left most (lm) point -% 2. Find the two points adjacent to that point -% The tripplet will make a triangle -% 3. Ensure no points lies within the triangle -% 4a.No points within triangle, -% make triangle, -% remove lm point -% 1. -% 4b.point(s) within triangle, -% - - -polygon_ls(Pts) -> - % Make triangles - Tris = polygon_tri(Pts), - % interval triangles - lists:flatten(polygon_tri_ls(Tris, [])). - -polygon_tri_ls([], Out) -> Out; -polygon_tri_ls([{P1,P2,P3}|Tris], Out) -> - polygon_tri_ls(Tris, [triangle_ls(P1,P2,P3)|Out]). - -polygon_tri(Pts) -> - polygon_tri(polygon_lm_pt(Pts), []). - - -polygon_tri([P1,P2,P3],Tris) -> [{P1,P2,P3}|Tris]; -polygon_tri([P2,P1,P3|Pts], Tris) -> - case polygon_tri_test(P1,P2,P3,Pts) of - false -> polygon_tri(polygon_lm_pt([P2,P3|Pts]), [{P1,P2,P3}|Tris]); - [LmPt|Ptsn] -> polygon_tri([P2,P1,LmPt,P3|Ptsn], Tris) - end. - -polygon_tri_test(P1,P2,P3, Pts) -> - polygon_tri_test(P1,P2,P3, Pts, []). - -polygon_tri_test(_,_,_, [], _) -> false; -polygon_tri_test(P1,P2,P3,[Pt|Pts], Ptsr) -> - case point_inside_triangle(Pt, P1,P2,P3) of - false -> polygon_tri_test(P1,P2,P3, Pts, [Pt|Ptsr]); - true -> [Pt|Pts] ++ lists:reverse(Ptsr) - end. - -% polygon_lm_pt -% In: -% Pts :: [{X,Y}] -% Out -% LmPts = [{X0,Y0},{Xmin,Y0},{X1,Y1},...] -% Purpose: -% The order of the list is important -% rotate the elements until Xmin is first -% This is not extremly fast. - -polygon_lm_pt(Pts) -> - Xs = [X||{X,_}<-Pts], - polygon_lm_pt(Pts, lists:min(Xs), []). - -polygon_lm_pt([Pt0,{X,_}=Ptm | Pts], Xmin, Ptsr) when X > Xmin -> - polygon_lm_pt([Ptm|Pts], Xmin, [Pt0|Ptsr]); -polygon_lm_pt(Pts, _, Ptsr) -> - Pts ++ lists:reverse(Ptsr). - - -% return true if P is inside triangle (p1,p2,p3), -% otherwise false. - -points_same_side({P1x,P1y}, {P2x,P2y}, {L1x,L1y}, {L2x,L2y}) -> - ((P1x - L1x)*(L2y - L1y) - (L2x - L1x)*(P1y - L1y) * - (P2x - L1x)*(L2y - L1y) - (L2x - L1x)*(P2y - L1y)) >= 0. - -point_inside_triangle(P, P1, P2, P3) -> - points_same_side(P, P1, P2, P3) and - points_same_side(P, P2, P1, P3) and - points_same_side(P, P3, P1, P2). - -%% [{Y, Xl, Xr}] -> #{Y := [{Xl,Xr}]} -%% Reorganize linspans to a map with Y as key. - -linespans_to_map(Ls) -> - linespans_to_map(Ls,#{}). -linespans_to_map([{Y,Xl,Xr,C}|Ls], M) -> - case M of - #{Y := Spans} -> linespans_to_map(Ls, M#{Y := [{Xl,Xr,C}|Spans]}); - _ -> linespans_to_map(Ls, M#{Y => [{Xl,Xr,C}]}) - end; -linespans_to_map([], M) -> - M. - - -%% line_to_linespans -%% Anti-aliased thick line -%% Do it CPS style -%% In: -%% P1 :: point() -%% P2 :: point() -%% Out: -%% [{Y,Xl,Xr}] -%% -line_to_linespans({X0,Y0},{X1,Y1},Wd) -> - Dx = abs(X1-X0), - Dy = abs(Y1-Y0), - Sx = if X0 < X1 -> 1; true -> -1 end, - Sy = if Y0 < Y1 -> 1; true -> -1 end, - E0 = Dx - Dy, - Ed = if Dx + Dy =:= 0 -> 1; true -> math:sqrt(Dx*Dx + Dy*Dy) end, - line_to_ls(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E0,Ed,(Wd+1)/2,[]). - -line_to_ls(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0) -> - C = max(0, 255*(abs(E - Dx+Dy)/Ed - Wd + 1)), - Ls1 = [{Y0,X0,X0,C}|Ls0], - line_to_ls_sx(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls1,E). - -line_to_ls_sx(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2) when 2*E2 > -Dx -> - line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2+Dy,Y0); -line_to_ls_sx(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2) -> - line_to_ls_sy(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2,X0). - -line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,E2,Y) when E2 < Ed*Wd andalso - (Y1 =/= Y orelse Dx > Dy) -> - Y2 = Y + Sy, - C = max(0,255*(abs(E2)/Ed-Wd+1)), - Ls = [{Y2,X0,X0,C}|Ls0], - line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2+Dx,Y2); -line_to_ls_sx_do(X0,_Y0,X1,_Y1,_Dx,_Dy,_Sx,_Sy,_E,_Ed,_Wd,Ls,_E2,_Y) when X0 =:= X1 -> - Ls; -line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,_E2,_Y) -> - line_to_ls_sy(X0+Sx,Y0,X1,Y1,Dx,Dy,Sx,Sy,E-Dy,Ed,Wd,Ls,E,X0). - -line_to_ls_sy(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,E2,X) when 2*E2 =< Dy -> - line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,Dx-E2,X); -line_to_ls_sy(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,_E2,_X) -> - line_to_ls(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0). - -line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,E2,X) when E2 < Ed*Wd andalso - (X1 =/= X orelse Dx < Dy) -> - X2 = X + Sx, - C = max(0,255*(abs(E2)/Ed-Wd+1)), - Ls = [{Y0,X2,X2,C}|Ls0], - line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2+Dy,X2); -line_to_ls_sy_do(_X0,Y0,_X1,Y1,_Dx,_Dy,_Sx,_Sy,_E,_Ed,_Wd,Ls,_E2,_X) when Y0 =:= Y1 -> - Ls; -line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,_E2,_X) -> - line_to_ls(X0,Y0+Sy,X1,Y1,Dx,Dy,Sx,Sy,E+Dx,Ed,Wd,Ls0). - -% Text - -text_horizontal_ls(Point, Font, Chars) -> - {_Fw,Fh} = egd_font:size(Font), - text_intervals(Point, Fh, Font, Chars, []). - -% This is stupid. The starting point is the top left (Ptl) but the font -% offsets is relative to the bottom right origin, -% {Xtl,Ytl} ------------------------- -% | | -% | Glyph BoundingBox | -% | -------- | -% | |Bitmap| Gh | -% FH |-Gx0-|Data | | -% | -------- | -% | | | -% | Gy0 | -% | | | -% Glyph (0,0)------------------------- Gxm (Glyph X move) -% FW -% Therefore, we need Yo, which is Yo = FH - Gy0 - Gh, -% Font height minus Glyph Y offset minus Glyph bitmap data boundingbox -% height. - -text_intervals( _, _, _, [], Out) -> lists:flatten(Out); -text_intervals({Xtl,Ytl}, Fh, Font, [Code|Chars], Out) -> - {{_Gw, Gh, Gx0, Gy0, Gxm}, LSs} = egd_font:glyph(Font, Code), - % Set offset points from translation matrix to point in TeInVe. - Yo = Fh - Gh + Gy0, - GLSs = text_intervals_vertical({Xtl+Gx0,Ytl+Yo},LSs, []), - text_intervals({Xtl+Gxm,Ytl}, Fh, Font, Chars, [GLSs|Out]). - -text_intervals_vertical( _, [], Out) -> Out; -text_intervals_vertical({Xtl, Ytl}, [LS|LSs], Out) -> - H = lists:foldl( - fun ({Xl,Xr}, RLSs) -> - [{Ytl, Xl + Xtl, Xr + Xtl}|RLSs] - end, [], LS), - text_intervals_vertical({Xtl, Ytl+1}, LSs, [H|Out]). - - -%%% E. PostScript implementation - -eps(#image{ objects = Os, width = W, height = H}) -> - list_to_binary([eps_header(W,H),eps_objects(H,Os),eps_footer()]). - -eps_objects(H,Os) -> eps_objects(H,Os, []). -eps_objects(_,[], Out) -> lists:flatten(Out); -eps_objects(H,[O|Os], Out) -> eps_objects(H,Os, [eps_object(H,O)|Out]). - -eps_object(H,#image_object{ type = text_horizontal, internals = {_Font,Text}, points = [{X,Y}], color={R,G,B,_}}) -> - s("/Times-Roman findfont\n14 scalefont\nsetfont\n~.4f ~.4f ~.4f setrgbcolor\nnewpath\n~p ~p moveto\n(~s) show~n", - [R,G,B,X,H-(Y + 10), Text]); -eps_object(H,#image_object{ type = filled_ellipse, points = [{X1,Y1p},{X2,Y2p}], color={R,G,B,_}}) -> - Y1 = H - Y1p, - Y2 = H - Y2p, - Xr = trunc((X2-X1)/2), - Yr = trunc((Y2-Y1)/2), - Cx = X1 + Xr, - Cy = Y1 + Yr, - s("~.4f ~.4f ~.4f setrgbcolor\nnewpath\n~p ~p ~p ~p 0 360 ellipse fill\n", - [R,G,B,Cx,Cy,Xr,Yr]); -eps_object(H,#image_object{ type = arc, points = [P0, P1], internals = D, color={R,G,B,_}}) -> - Es = egd_primitives:arc_to_edges(P0, P1, D), - [s("~.4f ~.4f ~.4f setrgbcolor\n", [R,G,B])|lists:foldl(fun - ({{X1,Y1},{X2,Y2}}, Eps) -> - [s("newpath\n~p ~p moveto\n~p ~p lineto\n1 setlinewidth\nstroke\n", [X1,H-Y1,X2,H-Y2])|Eps] - end, [], Es)]; - -eps_object(H,#image_object{ type = line, points = [{X1,Y1}, {X2,Y2}], color={R,G,B,_}}) -> - s("~.4f ~.4f ~.4f setrgbcolor\nnewpath\n~p ~p moveto\n~p ~p lineto\n1 setlinewidth\nstroke\n", - [R,G,B,X1,H-Y1,X2,H-Y2]); -eps_object(H,#image_object{ type = rectangle, points = [{X1,Y1}, {X2,Y2}], color={R,G,B,_}}) -> - s("~.4f ~.4f ~.4f setrgbcolor\nnewpath\n~p ~p moveto\n~p ~p lineto\n~p ~p lineto\n~p ~p lineto\n~p ~p lineto\n1 setlinewidth\nstroke\n", - [R,G,B,X1,H-Y1,X2,H-Y1,X2,H-Y2,X1,H-Y2,X1,H-Y1]); -eps_object(H,#image_object{ type = filled_rectangle, points = [{X1,Y1}, {X2,Y2}], color={R,G,B,_}}) -> - s("~.4f ~.4f ~.4f setrgbcolor\nnewpath\n~p ~p moveto\n~p ~p lineto\n~p ~p lineto\n~p ~p lineto\n~p ~p lineto\nclosepath\nfill\n", - [R,G,B,X1,H-Y1,X2,H-Y1,X2,H-Y2,X1,H-Y2,X1,H-Y1]); -eps_object(_,_) -> "". - -s(Format, Terms) -> lists:flatten(io_lib:format(Format, Terms)). - -eps_header(W,H) -> - s("%!PS-Adobe-3.0 EPSF-3.0\n%%Creator: Created by egd\n%%BoundingBox: 0 0 ~p ~p\n%%LanguageLevel: 2\n%%Pages: 1\n%%DocumentData: Clean7Bit\n",[W,H]) ++ - "%%BeginProlog\n/ellipse {7 dict begin\n/endangle exch def\n/startangle exch def\n/yradius exch def\n/xradius exch def\n/yC exch def\n/xC exch def\n" - "/savematrix matrix currentmatrix def\nxC yC translate\nxradius yradius scale\n0 0 1 startangle endangle arc\nsavematrix setmatrix\nend\n} def\n" - "%%EndProlog\n". - -eps_footer() -> - "%%EOF\n". diff --git a/lib/percept/src/percept.app.src b/lib/percept/src/percept.app.src deleted file mode 100644 index ab0d9a4d90..0000000000 --- a/lib/percept/src/percept.app.src +++ /dev/null @@ -1,45 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -{application,percept, [ - {description, "PERCEPT Erlang Concurrency Profiling Tool"}, - {vsn, "%VSN%"}, - {modules, [ - egd, - egd_font, - egd_png, - egd_primitives, - egd_render, - percept, - percept_analyzer, - percept_db, - percept_graph, - percept_html, - percept_image - ]}, - {registered, [percept_db,percept_port]}, - {applications, [kernel,stdlib]}, - {env,[]}, - {runtime_dependencies, ["stdlib-2.0","runtime_tools-1.8.14","kernel-3.0", - "inets-5.10","erts-9.0"]} -]}. - - -%% vim: syntax=erlang diff --git a/lib/percept/src/percept.appup.src b/lib/percept/src/percept.appup.src deleted file mode 100644 index 3ccdf8db2b..0000000000 --- a/lib/percept/src/percept.appup.src +++ /dev/null @@ -1,22 +0,0 @@ -%% -*- erlang -*- -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -{"%VSN%", - [{<<".*">>,[{restart_application, percept}]}], - [{<<".*">>,[{restart_application, percept}]}] -}. diff --git a/lib/percept/src/percept.erl b/lib/percept/src/percept.erl deleted file mode 100644 index 25c6ae19b1..0000000000 --- a/lib/percept/src/percept.erl +++ /dev/null @@ -1,337 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%% @doc Percept - Erlang Concurrency Profiling Tool -%% -%% This module provides the user interface for the application. -%% - --module(percept). --behaviour(application). --export([profile/1, - profile/2, - profile/3, - stop_profile/0, - start_webserver/0, - start_webserver/1, - stop_webserver/0, - stop_webserver/1, - analyze/1, - % Application behaviour - start/2, - stop/1]). - - --include("percept.hrl"). - -%%========================================================================== -%% Type definitions -%%========================================================================== - -%% @type percept_option() = procs | ports | exclusive - --type percept_option() :: 'procs' | 'ports' | 'exclusive' | 'scheduler'. - -%%========================================================================== -%% Application callback functions -%%========================================================================== - -%% @spec start(Type, Args) -> {started, Hostname, Port} | {error, Reason} -%% @doc none -%% @hidden - -start(_Type, _Args) -> - %% start web browser service - start_webserver(0). - -%% @spec stop(State) -> ok -%% @doc none -%% @hidden - -stop(_State) -> - %% stop web browser service - stop_webserver(0). - -%%========================================================================== -%% Interface functions -%%========================================================================== - -%% @spec profile(Filename::string()) -> {ok, Port} | {already_started, Port} -%% @see percept_profile - -%% profiling - --spec profile(Filename :: file:filename()) -> - {'ok', port()} | {'already_started', port()}. - -profile(Filename) -> - percept_profile:start(Filename, [procs]). - -%% @spec profile(Filename::string(), [percept_option()]) -> {ok, Port} | {already_started, Port} -%% @see percept_profile - --spec profile(Filename :: file:filename(), - Options :: [percept_option()]) -> - {'ok', port()} | {'already_started', port()}. - -profile(Filename, Options) -> - percept_profile:start(Filename, Options). - -%% @spec profile(Filename::string(), MFA::mfa(), [percept_option()]) -> ok | {already_started, Port} | {error, not_started} -%% @see percept_profile - --spec profile(Filename :: file:filename(), - Entry :: {atom(), atom(), list()}, - Options :: [percept_option()]) -> - 'ok' | {'already_started', port()} | {'error', 'not_started'}. - -profile(Filename, MFA, Options) -> - percept_profile:start(Filename, MFA, Options). - --spec stop_profile() -> 'ok' | {'error', 'not_started'}. - -%% @spec stop_profile() -> ok | {'error', 'not_started'} -%% @see percept_profile - -stop_profile() -> - percept_profile:stop(). - -%% @spec analyze(string()) -> ok | {error, Reason} -%% @doc Analyze file. - --spec analyze(Filename :: file:filename()) -> - 'ok' | {'error', any()}. - -analyze(Filename) -> - case percept_db:start() of - {started, DB} -> - parse_and_insert(Filename,DB); - {restarted, DB} -> - parse_and_insert(Filename,DB) - end. - -%% @spec start_webserver() -> {started, Hostname, Port} | {error, Reason} -%% Hostname = string() -%% Port = integer() -%% Reason = term() -%% @doc Starts webserver. - --spec start_webserver() -> - {'started', string(), pos_integer()} | {'error', any()}. - -start_webserver() -> - start_webserver(0). - -%% @spec start_webserver(integer()) -> {started, Hostname, AssignedPort} | {error, Reason} -%% Hostname = string() -%% AssignedPort = integer() -%% Reason = term() -%% @doc Starts webserver. If port number is 0, an available port number will -%% be assigned by inets. - --spec start_webserver(Port :: non_neg_integer()) -> - {'started', string(), pos_integer()} | {'error', any()}. - -start_webserver(Port) when is_integer(Port) -> - ok = ensure_loaded(percept), - case whereis(percept_httpd) of - undefined -> - {ok, Config} = get_webserver_config("percept", Port), - ok = application:ensure_started(inets), - case inets:start(httpd, Config) of - {ok, Pid} -> - AssignedPort = find_service_port_from_pid(inets:services_info(), Pid), - {ok, Host} = inet:gethostname(), - %% workaround until inets can get me a service from a name. - Mem = spawn(fun() -> service_memory({Pid,AssignedPort,Host}) end), - register(percept_httpd, Mem), - {started, Host, AssignedPort}; - {error, Reason} -> - {error, {inets, Reason}} - end; - _ -> - {error, already_started} - end. - -%% @spec stop_webserver() -> ok | {error, not_started} -%% @doc Stops webserver. - -stop_webserver() -> - case whereis(percept_httpd) of - undefined -> - {error, not_started}; - Pid -> - do_stop([], Pid) - end. - -do_stop([], Pid)-> - Pid ! {self(), get_port}, - Port = receive P -> P end, - do_stop(Port, Pid); -do_stop(Port, [])-> - case whereis(percept_httpd) of - undefined -> - {error, not_started}; - Pid -> - do_stop(Port, Pid) - end; -do_stop(Port, Pid)-> - case find_service_pid_from_port(inets:services_info(), Port) of - undefined -> - {error, not_started}; - Pid2 -> - Pid ! quit, - inets:stop(httpd, Pid2) - end. - -%% @spec stop_webserver(integer()) -> ok | {error, not_started} -%% @doc Stops webserver of the given port. -%% @hidden - -stop_webserver(Port) -> - do_stop(Port,[]). - -%%========================================================================== -%% Auxiliary functions -%%========================================================================== - -%% parse_and_insert - -parse_and_insert(Filename, DB) -> - io:format("Parsing: ~p ~n", [Filename]), - T0 = erlang:monotonic_time(millisecond), - Pid = dbg:trace_client(file, Filename, mk_trace_parser(self())), - Ref = erlang:monitor(process, Pid), - parse_and_insert_loop(Filename, Pid, Ref, DB, T0). - -parse_and_insert_loop(Filename, Pid, Ref, DB, T0) -> - receive - {'DOWN',Ref,process, Pid, noproc} -> - io:format("Incorrect file or malformed trace file: ~p~n", [Filename]), - {error, file}; - {parse_complete, {Pid, Count}} -> - receive {'DOWN', Ref, process, Pid, normal} -> ok after 0 -> ok end, - DB ! {action, consolidate}, - T1 = erlang:monotonic_time(millisecond), - io:format("Parsed ~w entries in ~w ms.~n", [Count, T1 - T0]), - io:format(" ~p created processes.~n", [length(percept_db:select({information, procs}))]), - io:format(" ~p opened ports.~n", [length(percept_db:select({information, ports}))]), - ok; - {'DOWN',Ref, process, Pid, normal} -> parse_and_insert_loop(Filename, Pid, Ref, DB, T0); - {'DOWN',Ref, process, Pid, Reason} -> {error, Reason} - end. - -mk_trace_parser(Pid) -> - {fun trace_parser/2, {0, Pid}}. - -trace_parser(end_of_trace, {Count, Pid}) -> - Pid ! {parse_complete, {self(),Count}}, - receive - {ack, Pid} -> - ok - end; -trace_parser(Trace, {Count, Pid}) -> - percept_db:insert(Trace), - {Count + 1, Pid}. - -find_service_pid_from_port([], _) -> - undefined; -find_service_pid_from_port([{_, Pid, Options} | Services], Port) -> - case lists:keyfind(port, 1, Options) of - false -> - find_service_pid_from_port(Services, Port); - {port, Port} -> - Pid - end. - -find_service_port_from_pid([], _) -> - undefined; -find_service_port_from_pid([{_, Pid, Options} | _], Pid) -> - case lists:keyfind(port, 1, Options) of - false -> - undefined; - {port, Port} -> - Port - end; -find_service_port_from_pid([{_, _, _} | Services], Pid) -> - find_service_port_from_pid(Services, Pid). - -%% service memory - -service_memory({Pid, Port, Host}) -> - receive - quit -> - ok; - {Reply, get_port} -> - Reply ! Port, - service_memory({Pid, Port, Host}); - {Reply, get_host} -> - Reply ! Host, - service_memory({Pid, Port, Host}); - {Reply, get_pid} -> - Reply ! Pid, - service_memory({Pid, Port, Host}) - end. - -% Create config data for the webserver - -get_webserver_config(Servername, Port) when is_list(Servername), is_integer(Port) -> - Path = code:priv_dir(percept), - Root = filename:join([Path, "server_root"]), - MimeTypesFile = filename:join([Root,"conf","mime.types"]), - {ok, MimeTypes} = httpd_conf:load_mime_types(MimeTypesFile), - Config = [ - % Roots - {server_root, Root}, - {document_root,filename:join([Root, "htdocs"])}, - - % Aliases - {eval_script_alias,{"/eval",[io]}}, - {erl_script_alias,{"/cgi-bin",[percept_graph,percept_html,io]}}, - {script_alias,{"/cgi-bin/", filename:join([Root, "cgi-bin"])}}, - {alias,{"/javascript/",filename:join([Root, "scripts"]) ++ "/"}}, - {alias,{"/images/", filename:join([Root, "images"]) ++ "/"}}, - {alias,{"/css/", filename:join([Root, "css"]) ++ "/"}}, - - % Configs - {default_type,"text/plain"}, - {directory_index,["index.html"]}, - {mime_types, MimeTypes}, - {modules,[mod_alias, - mod_esi, - mod_actions, - mod_cgi, - mod_dir, - mod_get, - mod_head - ]}, - {com_type,ip_comm}, - {server_name, Servername}, - {bind_address, any}, - {port, Port}], - {ok, Config}. - -ensure_loaded(App) -> - case application:load(App) of - ok -> ok; - {error,{already_loaded,App}} -> ok; - Error -> Error - end. diff --git a/lib/percept/src/percept.hrl b/lib/percept/src/percept.hrl deleted file mode 100644 index 58926cd1b4..0000000000 --- a/lib/percept/src/percept.hrl +++ /dev/null @@ -1,53 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - --define(seconds(EndTs,StartTs), timer:now_diff(EndTs, StartTs)/1000000). - -%%% ------------------- %%% -%%% Type definitions %%% -%%% ------------------- %%% - --type timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. --type true_mfa() :: {atom(), atom(), byte() | list()}. --type state() :: 'active' | 'inactive'. --type scheduler_id() :: {'scheduler_id', non_neg_integer()}. - -%%% ------------------- %%% -%%% Records %%% -%%% ------------------- %%% - --record(activity, { - timestamp ,%:: timestamp() , - id ,%:: pid() | port() | scheduler_id(), - state = undefined ,%:: state() | 'undefined', - where = undefined ,%:: true_mfa() | 'undefined', - runnable_count = 0 %:: non_neg_integer() - }). - --record(information, { - id ,%:: pid() | port(), - name = undefined ,%:: atom() | string() | 'undefined', - entry = undefined ,%:: true_mfa() | 'undefined', - start = undefined ,%:: timestamp() | 'undefined', - stop = undefined ,%:: timestamp() | 'undefined', - parent = undefined ,%:: pid() | 'undefined', - children = [] %:: [pid()] - }). - diff --git a/lib/percept/src/percept_analyzer.erl b/lib/percept/src/percept_analyzer.erl deleted file mode 100644 index f38d026905..0000000000 --- a/lib/percept/src/percept_analyzer.erl +++ /dev/null @@ -1,368 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - -%% @doc Utility functions to operate on percept data. These functions should -%% be considered experimental. Behaviour may change in future releases. - --module(percept_analyzer). --export([ - minmax/1, - waiting_activities/1, - activities2count/2, - activities2count/3, - activities2count2/2, - analyze_activities/2, - runnable_count/1, - runnable_count/2, - seconds2ts/2, - minmax_activities/2, - mean/1 - ]). - --include("percept.hrl"). - -%%========================================================================== -%% -%% Interface functions -%% -%%========================================================================== - - -%% @spec minmax([{X, Y}]) -> {MinX, MinY, MaxX, MaxY} -%% X = number() -%% Y = number() -%% MinX = number() -%% MinY = number() -%% MaxX = number() -%% MaxY = number() -%% @doc Returns the min and max of a set of 2-dimensional numbers. - -minmax(Data) -> - Xs = [ X || {X,_Y} <- Data], - Ys = [ Y || {_X, Y} <- Data], - {lists:min(Xs), lists:min(Ys), lists:max(Xs), lists:max(Ys)}. - -%% @spec mean([number()]) -> {Mean, StdDev, N} -%% Mean = float() -%% StdDev = float() -%% N = integer() -%% @doc Calculates the mean and the standard deviation of a set of -%% numbers. - -mean([]) -> {0, 0, 0}; -mean([Value]) -> {Value, 0, 1}; -mean(List) -> mean(List, {0, 0, 0}). - -mean([], {Sum, SumSquare, N}) -> - Mean = Sum / N, - StdDev = math:sqrt((SumSquare - Sum*Sum/N)/(N - 1)), - {Mean, StdDev, N}; -mean([Value | List], {Sum, SumSquare, N}) -> - mean(List, {Sum + Value, SumSquare + Value*Value, N + 1}). - - - -activities2count2(Acts, StartTs) -> - Start = inactive_start_states(Acts), - activities2count2(Acts, StartTs, Start, []). - -activities2count2([], _, _, Out) -> lists:reverse(Out); -activities2count2([#activity{ id = Id, timestamp = Ts, state = active} | Acts], StartTs, {Proc,Port}, Out) when is_pid(Id) -> - activities2count2(Acts, StartTs, {Proc + 1, Port}, [{?seconds(Ts, StartTs), Proc + 1, Port}|Out]); -activities2count2([#activity{ id = Id, timestamp = Ts, state = inactive} | Acts], StartTs, {Proc,Port}, Out) when is_pid(Id) -> - activities2count2(Acts, StartTs, {Proc - 1, Port}, [{?seconds(Ts, StartTs), Proc - 1, Port}|Out]); -activities2count2([#activity{ id = Id, timestamp = Ts, state = active} | Acts], StartTs, {Proc,Port}, Out) when is_port(Id) -> - activities2count2(Acts, StartTs, {Proc, Port + 1}, [{?seconds(Ts, StartTs), Proc, Port + 1}|Out]); -activities2count2([#activity{ id = Id, timestamp = Ts, state = inactive} | Acts], StartTs, {Proc,Port}, Out) when is_port(Id) -> - activities2count2(Acts, StartTs, {Proc, Port - 1}, [{?seconds(Ts, StartTs), Proc, Port - 1}|Out]). - - -inactive_start_states(Acts) -> - D = activity_start_states(Acts, dict:new()), - dict:fold(fun - (K, inactive, {Procs, Ports}) when is_pid(K) -> {Procs + 1, Ports}; - (K, inactive, {Procs, Ports}) when is_port(K) -> {Procs, Ports + 1}; - (_, _, {Procs, Ports}) -> {Procs, Ports} - end, {0,0}, D). -activity_start_states([], D) -> D; -activity_start_states([#activity{id = Id, state = State}|Acts], D) -> - case dict:is_key(Id, D) of - true -> activity_start_states(Acts, D); - false -> activity_start_states(Acts, dict:store(Id, State, D)) - end. - - - - -%% @spec activities2count(#activity{}, timestamp()) -> Result -%% Result = [{Time, ProcessCount, PortCount}] -%% Time = float() -%% ProcessCount = integer() -%% PortCount = integer() -%% @doc Calculate the resulting active processes and ports during -%% the activity interval. -%% Also checks active/inactive consistency. -%% A task will always begin with an active state and end with an inactive state. - -activities2count(Acts, StartTs) when is_list(Acts) -> activities2count(Acts, StartTs, separated). - -activities2count(Acts, StartTs, Type) when is_list(Acts) -> activities2count_loop(Acts, {StartTs, {0,0}}, Type, []). - -activities2count_loop([], _, _, Out) -> lists:reverse(Out); -activities2count_loop( - [#activity{ timestamp = Ts, id = Id, runnable_count = Rc} | Acts], - {StartTs, {Procs, Ports}}, separated, Out) -> - - Time = ?seconds(Ts, StartTs), - case Id of - Id when is_port(Id) -> - Entry = {Time, Procs, Rc}, - activities2count_loop(Acts, {StartTs, {Procs, Rc}}, separated, [Entry | Out]); - Id when is_pid(Id) -> - Entry = {Time, Rc, Ports}, - activities2count_loop(Acts, {StartTs, {Rc, Ports}}, separated, [Entry | Out]); - _ -> - activities2count_loop(Acts, {StartTs,{Procs, Ports}}, separated, Out) - end; -activities2count_loop( - [#activity{ timestamp = Ts, id = Id, runnable_count = Rc} | Acts], - {StartTs, {Procs, Ports}}, summated, Out) -> - - Time = ?seconds(Ts, StartTs), - case Id of - Id when is_port(Id) -> - Entry = {Time, Procs + Rc}, - activities2count_loop(Acts, {StartTs, {Procs, Rc}}, summated, [Entry | Out]); - Id when is_pid(Id) -> - Entry = {Time, Rc + Ports}, - activities2count_loop(Acts, {StartTs, {Rc, Ports}}, summated, [Entry | Out]) - end. - -%% @spec waiting_activities([#activity{}]) -> FunctionList -%% FunctionList = [{Seconds, Mfa, {Mean, StdDev, N}}] -%% Seconds = float() -%% Mfa = mfa() -%% Mean = float() -%% StdDev = float() -%% N = integer() -%% @doc Calculates the time, both average and total, that a process has spent -%% in a receive state at specific function. However, if there are multiple receives -%% in a function it cannot differentiate between them. - -waiting_activities(Activities) -> - ListedMfas = waiting_activities_mfa_list(Activities, []), - Unsorted = lists:foldl( - fun (Mfa, MfaList) -> - {Total, WaitingTimes} = get({waiting_mfa, Mfa}), - - % cleanup - erlang:erase({waiting_mfa, Mfa}), - - % statistics of receive waiting places - Stats = mean(WaitingTimes), - - [{Total, Mfa, Stats} | MfaList] - end, [], ListedMfas), - lists:sort(fun ({A,_,_},{B,_,_}) -> - if - A > B -> true; - true -> false - end - end, Unsorted). - - -%% Generate lists of receive waiting times per mfa -%% Out: -%% ListedMfas = [mfa()] -%% Intrisnic: -%% get({waiting, mfa()}) -> -%% [{waiting, mfa()}, {Total, [WaitingTime]}) -%% WaitingTime = float() - -waiting_activities_mfa_list([], ListedMfas) -> ListedMfas; -waiting_activities_mfa_list([Activity|Activities], ListedMfas) -> - #activity{id = Pid, state = Act, timestamp = Time, where = MFA} = Activity, - case Act of - active -> - waiting_activities_mfa_list(Activities, ListedMfas); - inactive -> - % Want to know how long the wait is in a receive, - % it is given via the next activity - case Activities of - [] -> - [Info] = percept_db:select(information, Pid), - case Info#information.stop of - undefined -> - % get profile end time - Waited = ?seconds( - percept_db:select({system,stop_ts}), - Time); - Time2 -> - Waited = ?seconds(Time2, Time) - end, - case get({waiting_mfa, MFA}) of - undefined -> - put({waiting_mfa, MFA}, {Waited, [Waited]}), - [MFA | ListedMfas]; - {Total, TimedMfa} -> - put({waiting_mfa, MFA}, {Total + Waited, [Waited | TimedMfa]}), - ListedMfas - end; - [#activity{timestamp=Time2, id = Pid, state = active} | _ ] -> - % Calculate waiting time - Waited = ?seconds(Time2, Time), - % Get previous entry - - case get({waiting_mfa, MFA}) of - undefined -> - % add entry to list - put({waiting_mfa, MFA}, {Waited, [Waited]}), - waiting_activities_mfa_list(Activities, [MFA|ListedMfas]); - {Total, TimedMfa} -> - put({waiting_mfa, MFA}, {Total + Waited, [Waited | TimedMfa]}), - waiting_activities_mfa_list(Activities, ListedMfas) - end; - _ -> error - end - end. - -%% seconds2ts(Seconds, StartTs) -> TS -%% In: -%% Seconds = float() -%% StartTs = timestamp() -%% Out: -%% TS = timestamp() - -%% @spec seconds2ts(float(), StartTs::{integer(),integer(),integer()}) -> timestamp() -%% @doc Calculates a timestamp given a duration in seconds and a starting timestamp. - -seconds2ts(Seconds, {Ms, S, Us}) -> - % Calculate mega seconds integer - MsInteger = trunc(Seconds) div 1000000 , - - % Calculate the reminder for seconds - SInteger = trunc(Seconds), - - % Calculate the reminder for micro seconds - UsInteger = trunc((Seconds - SInteger) * 1000000), - - % Wrap overflows - - UsOut = (UsInteger + Us) rem 1000000, - SOut = ((SInteger + S) + (UsInteger + Us) div 1000000) rem 1000000, - MsOut = (MsInteger+ Ms) + ((SInteger + S) + (UsInteger + Us) div 1000000) div 1000000, - - {MsOut, SOut, UsOut}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% Analyze interval for concurrency -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% @spec analyze_activities(integer(), [#activity{}]) -> [{integer(),#activity{}}] -%% @hidden - -analyze_activities(Threshold, Activities) -> - RunnableCount = runnable_count(Activities, 0), - analyze_runnable_activities(Threshold, RunnableCount). - - -%% runnable_count(Activities, StartValue) -> RunnableCount -%% In: -%% Activities = [activity()] -%% StartValue = integer() -%% Out: -%% RunnableCount = [{integer(), activity()}] -%% Purpose: -%% Calculate the runnable count of a given interval of generic -%% activities. - -%% @spec runnable_count([#activity{}]) -> [{integer(),#activity{}}] -%% @hidden - -runnable_count(Activities) -> - Threshold = runnable_count_threshold(Activities), - runnable_count(Activities, Threshold, []). - -runnable_count_threshold(Activities) -> - CountedActs = runnable_count(Activities, 0), - Counts = [C || {C, _} <- CountedActs], - Min = lists:min(Counts), - 0 - Min. -%% @spec runnable_count([#activity{}],integer()) -> [{integer(),#activity{}}] -%% @hidden - -runnable_count(Activities, StartCount) when is_integer(StartCount) -> - runnable_count(Activities, StartCount, []). -runnable_count([], _ , Out) -> - lists:reverse(Out); -runnable_count([A | As], PrevCount, Out) -> - case A#activity.state of - active -> - runnable_count(As, PrevCount + 1, [{PrevCount + 1, A} | Out]); - inactive -> - runnable_count(As, PrevCount - 1, [{PrevCount - 1, A} | Out]) - end. - -%% In: -%% Threshold = integer(), -%% RunnableActivities = [{Rc, activity()}] -%% Rc = integer() - -analyze_runnable_activities(Threshold, RunnableActivities) -> - analyze_runnable_activities(Threshold, RunnableActivities, []). - -analyze_runnable_activities( _z, [], Out) -> - lists:reverse(Out); -analyze_runnable_activities(Threshold, [{Rc, Act} | RunnableActs], Out) -> - if - Rc =< Threshold -> - analyze_runnable_activities(Threshold, RunnableActs, [{Rc,Act} | Out]); - true -> - analyze_runnable_activities(Threshold, RunnableActs, Out) - end. - -%% minmax_activity(Activities, Count) -> {Min, Max} -%% In: -%% Activities = [activity()] -%% InitialCount = non_neg_integer() -%% Out: -%% {Min, Max} -%% Min = non_neg_integer() -%% Max = non_neg_integer() -%% Purpose: -%% Minimal and maximal activity during an activity interval. -%% Initial activity count needs to be supplied. - -%% @spec minmax_activities([#activity{}], integer()) -> {integer(), integer()} -%% @doc Calculates the minimum and maximum of runnable activites (processes -% and ports) during the interval of reffered by the activity list. - -minmax_activities(Activities, Count) -> - minmax_activities(Activities, Count, {Count, Count}). -minmax_activities([], _, Out) -> - Out; -minmax_activities([A|Acts], Count, {Min, Max}) -> - case A#activity.state of - active -> - minmax_activities(Acts, Count + 1, {Min, lists:max([Count + 1, Max])}); - inactive -> - minmax_activities(Acts, Count - 1, {lists:min([Count - 1, Min]), Max}) - end. diff --git a/lib/percept/src/percept_db.erl b/lib/percept/src/percept_db.erl deleted file mode 100644 index 6cbe3ce022..0000000000 --- a/lib/percept/src/percept_db.erl +++ /dev/null @@ -1,780 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - -%% -%% @doc Percept database. -%% -%% - --module(percept_db). - --export([start/0, - stop/0, - insert/1, - select/2, - select/1, - consolidate/0]). - --include("percept.hrl"). --define(STOP_TIMEOUT, 1000). -%%========================================================================== -%% Type definitions -%%========================================================================== - -%% @type activity_option() = -%% {ts_min, timestamp()} | -%% {ts_max, timestamp()} | -%% {ts_exact, bool()} | -%% {mfa, {atom(), atom(), byte()}} | -%% {state, active | inactive} | -%% {id, all | procs | ports | pid() | port()} - -%% @type scheduler_option() = -%% {ts_min, timestamp()} | -%% {ts_max, timestamp()} | -%% {ts_exact, bool()} | -%% {id, scheduler_id()} - -%% @type system_option() = start_ts | stop_ts - -%% @type information_option() = -%% all | procs | ports | pid() | port() - - - - -%%========================================================================== -%% Interface functions -%%========================================================================== - -%% @spec start() -> ok | {started, Pid} | {restarted, Pid} -%% Pid = pid() -%% @doc Starts or restarts the percept database. - --spec start() -> {'started', pid()} | {'restarted', pid()}. - -start() -> - case erlang:whereis(percept_db) of - undefined -> - {started, do_start()}; - PerceptDB -> - {restarted, restart(PerceptDB)} - end. - -%% @spec restart(pid()) -> pid() -%% @private -%% @doc restarts the percept database. - --spec restart(pid())-> pid(). - -restart(PerceptDB)-> - stop_sync(PerceptDB), - do_start(). - -%% @spec do_start() -> pid() -%% @private -%% @doc starts the percept database. - --spec do_start()-> pid(). - -do_start()-> - Pid = spawn(fun() -> init_percept_db() end), - erlang:register(percept_db, Pid), - Pid. - -%% @spec stop() -> not_started | {stopped, Pid} -%% Pid = pid() -%% @doc Stops the percept database. - --spec stop() -> 'not_started' | {'stopped', pid()}. - -stop() -> - case erlang:whereis(percept_db) of - undefined -> - not_started; - Pid -> - Pid ! {action, stop}, - {stopped, Pid} - end. - -%% @spec stop_sync(pid()) -> true -%% @private -%% @doc Stops the percept database, with a synchronous call. - --spec stop_sync(pid()) -> true. - -stop_sync(Pid) -> - MonitorRef = erlang:monitor(process, Pid), - _ = stop(), - receive - {'DOWN', MonitorRef, _Type, Pid, _Info}-> - true - after ?STOP_TIMEOUT-> - erlang:demonitor(MonitorRef, [flush]), - exit(Pid, kill) - end. - -%% @spec insert(tuple()) -> ok -%% @doc Inserts a trace or profile message to the database. - -insert(Trace) -> - percept_db ! {insert, Trace}, - ok. - - -%% @spec select({atom(), Options}) -> Result -%% @doc Synchronous call. Selects information based on a query. -%% -%% <p>Queries:</p> -%% <pre> -%% {system, Option} -%% Option = system_option() -%% Result = timestamp() -%% {information, Options} -%% Options = [information_option()] -%% Result = [#information{}] -%% {scheduler, Options} -%% Options = [sceduler_option()] -%% Result = [#activity{}] -%% {activity, Options} -%% Options = [activity_option()] -%% Result = [#activity{}] -%% </pre> -%% <p> -%% Note: selection of Id's are always OR all other options are considered AND. -%% </p> - -select(Query) -> - percept_db ! {select, self(), Query}, - receive {result, Match} -> Match end. - -%% @spec select(atom(), list()) -> Result -%% @equiv select({Table,Options}) - -select(Table, Options) -> - percept_db ! {select, self(), {Table, Options}}, - receive {result, Match} -> Match end. - -%% @spec consolidate() -> Result -%% @doc Checks timestamp and state-flow inconsistencies in the -%% the database. - -consolidate() -> - percept_db ! {action, consolidate}, - ok. - -%%========================================================================== -%% Database loop -%%========================================================================== - -init_percept_db() -> - % Proc and Port information - pdb_info = ets:new(pdb_info, [named_table, private, {keypos, #information.id}, set]), - - % Scheduler runnability - pdb_scheduler = ets:new(pdb_scheduler, [named_table, private, {keypos, #activity.timestamp}, ordered_set]), - - % Process and Port runnability - pdb_activity = ets:new(pdb_activity, [named_table, private, {keypos, #activity.timestamp}, ordered_set]), - - % System status - pdb_system = ets:new(pdb_system, [named_table, private, {keypos, 1}, set]), - - % System warnings - pdb_warnings = ets:new(pdb_warnings, [named_table, private, {keypos, 1}, ordered_set]), - put(debug, 0), - loop_percept_db(). - -loop_percept_db() -> - receive - {insert, Trace} -> - insert_trace(clean_trace(Trace)), - loop_percept_db(); - {select, Pid, Query} -> - Pid ! {result, select_query(Query)}, - loop_percept_db(); - {action, stop} -> - stopped; - {action, consolidate} -> - consolidate_db(), - loop_percept_db(); - {operate, Pid, {Table, {Fun, Start}}} -> - Result = ets:foldl(Fun, Start, Table), - Pid ! {result, Result}, - loop_percept_db(); - Unhandled -> - io:format("loop_percept_db, unhandled query: ~p~n", [Unhandled]), - loop_percept_db() - end. - -%%========================================================================== -%% Auxiliary functions -%%========================================================================== - -%% cleans trace messages from external pids - -clean_trace(Trace) when is_tuple(Trace) -> list_to_tuple(clean_trace(tuple_to_list(Trace))); -clean_trace(Trace) when is_list(Trace) -> clean_list(Trace, []); -clean_trace(Trace) when is_pid(Trace) -> - PidStr = pid_to_list(Trace), - [_,P2,P3p] = string:tokens(PidStr,"."), - P3 = lists:sublist(P3p, 1, length(P3p) - 1), - erlang:list_to_pid("<0." ++ P2 ++ "." ++ P3 ++ ">"); -clean_trace(Trace) -> Trace. - -clean_list([], Out) -> lists:reverse(Out); -clean_list([Element|Trace], Out) -> - clean_list(Trace, [clean_trace(Element)|Out]). - - -insert_trace(Trace) -> - case Trace of - {profile_start, Ts} -> - update_system_start_ts(Ts), - ok; - {profile_stop, Ts} -> - update_system_stop_ts(Ts), - ok; - %%% erlang:system_profile, option: runnable_procs - %%% --------------------------------------------- - {profile, Id, State, Mfa, TS} when is_pid(Id) -> - % Update runnable count in activity and db - - case check_activity_consistency(Id, State) of - invalid_state -> - ignored; - ok -> - Rc = get_runnable_count(procs, State), - % Update registered procs - % insert proc activity - update_activity(#activity{ - id = Id, - state = State, - timestamp = TS, - runnable_count = Rc, - where = Mfa}), - ok - end; - %%% erlang:system_profile, option: runnable_ports - %%% --------------------------------------------- - {profile, Id, State, Mfa, TS} when is_port(Id) -> - case check_activity_consistency(Id, State) of - invalid_state -> - ignored; - ok -> - % Update runnable count in activity and db - Rc = get_runnable_count(ports, State), - - % Update registered ports - % insert port activity - update_activity(#activity{ - id = Id, - state = State, - timestamp = TS, - runnable_count = Rc, - where = Mfa}), - - ok - end; - %%% erlang:system_profile, option: scheduler - {profile, scheduler, Id, State, Scheds, Ts} -> - % insert scheduler activity - update_scheduler(#activity{ - id = {scheduler, Id}, - state = State, - timestamp = Ts, - where = Scheds}), - ok; - - %%% erlang:trace, option: procs - %%% --------------------------- - {trace_ts, Parent, spawn, Pid, Mfa, TS} -> - InformativeMfa = mfa2informative(Mfa), - % Update id_information - update_information(#information{id = Pid, start = TS, parent = Parent, entry = InformativeMfa}), - update_information_child(Parent, Pid), - ok; - {trace_ts, Pid, exit, _Reason, TS} -> - % Update registered procs - - % Update id_information - update_information(#information{id = Pid, stop = TS}), - - ok; - {trace_ts, Pid, register, Name, _Ts} when is_pid(Pid) -> - % Update id_information - update_information(#information{id = Pid, name = Name}), - ok; - {trace_ts, Pid, register, Name, _Ts} when is_pid(Pid) -> - % Update id_information - update_information(#information{id = Pid, name = Name}), - ok; - {trace_ts, _Pid, unregister, _Name, _Ts} -> - % Not implemented - ok; - {trace_ts, Pid, getting_unlinked, _Id, _Ts} when is_pid(Pid) -> - % Update id_information - ok; - {trace_ts, Pid, getting_linked, _Id, _Ts} when is_pid(Pid)-> - % Update id_information - ok; - {trace_ts, Pid, link, _Id, _Ts} when is_pid(Pid)-> - % Update id_information - ok; - {trace_ts, Pid, unlink, _Id, _Ts} when is_pid(Pid) -> - % Update id_information - ok; - - %%% erlang:trace, option: ports - %%% ---------------------------- - {trace_ts, Caller, open, Port, Driver, TS} -> - % Update id_information - update_information(#information{ - id = Port, entry = Driver, start = TS, parent = Caller}), - ok; - {trace_ts, Port, closed, _Reason, Ts} -> - % Update id_information - update_information(#information{id = Port, stop = Ts}), - ok; - - Unhandled -> - io:format("insert_trace, unhandled: ~p~n", [Unhandled]) - end. - -mfa2informative({erlang, apply, [M, F, Args]}) -> mfa2informative({M, F,Args}); -mfa2informative({erlang, apply, [Fun, Args]}) -> - FunInfo = erlang:fun_info(Fun), - M = case proplists:get_value(module, FunInfo, undefined) of - [] -> undefined_fun_module; - undefined -> undefined_fun_module; - Module -> Module - end, - F = case proplists:get_value(name, FunInfo, undefined) of - [] -> undefined_fun_function; - undefined -> undefined_fun_function; - Function -> Function - end, - mfa2informative({M, F, Args}); -mfa2informative(Mfa) -> Mfa. - -%% consolidate_db() -> bool() -%% Purpose: -%% Check start/stop time -%% Activity consistency - -consolidate_db() -> - io:format("Consolidating...~n"), - % Check start/stop timestamps - case select_query({system, start_ts}) of - undefined -> - Min = lists:min(list_all_ts()), - update_system_start_ts(Min); - _ -> ok - end, - case select_query({system, stop_ts}) of - undefined -> - Max = lists:max(list_all_ts()), - update_system_stop_ts(Max); - _ -> ok - end, - consolidate_runnability(), - ok. - -consolidate_runnability() -> - put({runnable, procs}, undefined), - put({runnable, ports}, undefined), - consolidate_runnability_loop(ets:first(pdb_activity)). - -consolidate_runnability_loop('$end_of_table') -> ok; -consolidate_runnability_loop(Key) -> - case ets:lookup(pdb_activity, Key) of - [#activity{id = Id, state = State } = A] when is_pid(Id) -> - Rc = get_runnable_count(procs, State), - ets:insert(pdb_activity, A#activity{ runnable_count = Rc}); - [#activity{id = Id, state = State } = A] when is_port(Id) -> - Rc = get_runnable_count(ports, State), - ets:insert(pdb_activity, A#activity{ runnable_count = Rc}); - _ -> throw(consolidate) - end, - consolidate_runnability_loop(ets:next(pdb_activity, Key)). - -list_all_ts() -> - ATs = [Act#activity.timestamp || Act <- select_query({activity, []})], - STs = [Act#activity.timestamp || Act <- select_query({scheduler, []})], - ITs = lists:flatten([ - [I#information.start, - I#information.stop] || - I <- select_query({information, all})]), - %% Filter out all undefined (non ts) - [Elem || Elem = {_,_,_} <- ATs ++ STs ++ ITs]. - -%% get_runnable_count(Type, State) -> RunnableCount -%% In: -%% Type = procs | ports -%% State = active | inactive -%% Out: -%% RunnableCount = integer() -%% Purpose: -%% Keep track of the number of runnable ports and processes -%% during the profile duration. - -get_runnable_count(Type, State) -> - case {get({runnable, Type}), State} of - {undefined, active} -> - put({runnable, Type}, 1), - 1; - {N, active} -> - put({runnable, Type}, N + 1), - N + 1; - {N, inactive} -> - put({runnable, Type}, N - 1), - N - 1; - Unhandled -> - io:format("get_runnable_count, unhandled ~p~n", [Unhandled]), - Unhandled - end. - -check_activity_consistency(Id, State) -> - case get({previous_state, Id}) of - State -> - io:format("check_activity_consistency, state flow invalid.~n"), - invalid_state; - undefined when State == inactive -> - invalid_state; - _ -> - put({previous_state, Id}, State), - ok - end. -%%% -%%% select_query -%%% In: -%%% Query = {Table, Option} -%%% Table = system | activity | scheduler | information - - -select_query(Query) -> - case Query of - {system, _ } -> - select_query_system(Query); - {activity, _ } -> - select_query_activity(Query); - {scheduler, _} -> - select_query_scheduler(Query); - {information, _ } -> - select_query_information(Query); - Unhandled -> - io:format("select_query, unhandled: ~p~n", [Unhandled]), - [] - end. - -%%% select_query_information - -select_query_information(Query) -> - case Query of - {information, all} -> - ets:select(pdb_info, [{ - #information{ _ = '_'}, - [], - ['$_'] - }]); - {information, procs} -> - ets:select(pdb_info, [{ - #information{ id = '$1', _ = '_'}, - [{is_pid, '$1'}], - ['$_'] - }]); - {information, ports} -> - ets:select(pdb_info, [{ - #information{ id = '$1', _ = '_'}, - [{is_port, '$1'}], - ['$_'] - }]); - {information, Id} when is_port(Id) ; is_pid(Id) -> - ets:select(pdb_info, [{ - #information{ id = Id, _ = '_'}, - [], - ['$_'] - }]); - Unhandled -> - io:format("select_query_information, unhandled: ~p~n", [Unhandled]), - [] - end. - -%%% select_query_scheduler - -select_query_scheduler(Query) -> - case Query of - {scheduler, Options} when is_list(Options) -> - Head = #activity{ - timestamp = '$1', - id = '$2', - state = '$3', - where = '$4', - _ = '_'}, - Body = ['$_'], - % We don't need id's - {Constraints, _ } = activity_ms_and(Head, Options, [], []), - ets:select(pdb_scheduler, [{Head, Constraints, Body}]); - Unhandled -> - io:format("select_query_scheduler, unhandled: ~p~n", [Unhandled]), - [] - end. - -%%% select_query_system - -select_query_system(Query) -> - case Query of - {system, start_ts} -> - case ets:lookup(pdb_system, {system, start_ts}) of - [] -> undefined; - [{{system, start_ts}, StartTS}] -> StartTS - end; - {system, stop_ts} -> - case ets:lookup(pdb_system, {system, stop_ts}) of - [] -> undefined; - [{{system, stop_ts}, StopTS}] -> StopTS - end; - Unhandled -> - io:format("select_query_system, unhandled: ~p~n", [Unhandled]), - [] - end. - -%%% select_query_activity - -select_query_activity(Query) -> - case Query of - {activity, Options} when is_list(Options) -> - case lists:member({ts_exact, true},Options) of - true -> - case catch select_query_activity_exact_ts(Options) of - {'EXIT', Reason} -> - io:format(" - select_query_activity [ catch! ]: ~p~n", [Reason]), - []; - Match -> - Match - end; - false -> - MS = activity_ms(Options), - case catch ets:select(pdb_activity, MS) of - {'EXIT', Reason} -> - io:format(" - select_query_activity [ catch! ]: ~p~n", [Reason]), - []; - Match -> - Match - end - end; - Unhandled -> - io:format("select_query_activity, unhandled: ~p~n", [Unhandled]), - [] - end. - -select_query_activity_exact_ts(Options) -> - case { proplists:get_value(ts_min, Options, undefined), proplists:get_value(ts_max, Options, undefined) } of - {undefined, undefined} -> []; - {undefined, _ } -> []; - {_ , undefined} -> []; - {TsMin , TsMax } -> - % Remove unwanted options - Opts = lists_filter([ts_exact], Options), - Ms = activity_ms(Opts), - case ets:select(pdb_activity, Ms) of - % no entries within interval - [] -> - Opts2 = lists_filter([ts_max, ts_min], Opts) ++ [{ts_min, TsMax}], - Ms2 = activity_ms(Opts2), - case ets:select(pdb_activity, Ms2, 1) of - '$end_of_table' -> []; - {[E], _} -> - [PrevAct] = ets:lookup(pdb_activity, ets:prev(pdb_activity, E#activity.timestamp)), - [PrevAct#activity{ timestamp = TsMin} , E] - end; - Acts -> - [Head| _] = Acts, - if - Head#activity.timestamp == TsMin -> Acts; - true -> - PrevTs = ets:prev(pdb_activity, Head#activity.timestamp), - case ets:lookup(pdb_activity, PrevTs) of - [] -> Acts; - [PrevAct] -> [PrevAct#activity{timestamp = TsMin}|Acts] - end - end - end - end. - -lists_filter([], Options) -> Options; -lists_filter([D|Ds], Options) -> - lists_filter(Ds, lists:filter( - fun ({Pred, _}) -> - if - Pred == D -> false; - true -> true - end - end, Options)). - -% Options: -% {ts_min, timestamp()} -% {ts_max, timestamp()} -% {mfa, mfa()} -% {state, active | inactive} -% {id, all | procs | ports | pid() | port()} -% -% All options are regarded as AND expect id which are regarded as OR -% For example: [{ts_min, TS1}, {ts_max, TS2}, {id, PID1}, {id, PORT1}] would be -% ({ts_min, TS1} and {ts_max, TS2} and {id, PID1}) or -% ({ts_min, TS1} and {ts_max, TS2} and {id, PORT1}). - -activity_ms(Opts) -> - % {activity, Timestamp, State, Mfa} - Head = #activity{ - timestamp = '$1', - id = '$2', - state = '$3', - where = '$4', - _ = '_'}, - - {Conditions, IDs} = activity_ms_and(Head, Opts, [], []), - Body = ['$_'], - - lists:foldl( - fun (Option, MS) -> - case Option of - {id, ports} -> - [{Head, [{is_port, Head#activity.id} | Conditions], Body} | MS]; - {id, procs} -> - [{Head,[{is_pid, Head#activity.id} | Conditions], Body} | MS]; - {id, ID} when is_pid(ID) ; is_port(ID) -> - [{Head,[{'==', Head#activity.id, ID} | Conditions], Body} | MS]; - {id, all} -> - [{Head, Conditions,Body} | MS]; - _ -> - io:format("activity_ms id dropped ~p~n", [Option]), - MS - end - end, [], IDs). - -activity_ms_and(_, [], Constraints, []) -> - {Constraints, [{id, all}]}; -activity_ms_and(_, [], Constraints, IDs) -> - {Constraints, IDs}; -activity_ms_and(Head, [Opt|Opts], Constraints, IDs) -> - case Opt of - {ts_min, Min} -> - activity_ms_and(Head, Opts, - [{'>=', Head#activity.timestamp, {Min}} | Constraints], IDs); - {ts_max, Max} -> - activity_ms_and(Head, Opts, - [{'=<', Head#activity.timestamp, {Max}} | Constraints], IDs); - {id, ID} -> - activity_ms_and(Head, Opts, - Constraints, [{id, ID} | IDs]); - {state, State} -> - activity_ms_and(Head, Opts, - [{'==', Head#activity.state, State} | Constraints], IDs); - {mfa, Mfa} -> - activity_ms_and(Head, Opts, - [{'==', Head#activity.where, {Mfa}}| Constraints], IDs); - _ -> - io:format("activity_ms_and option dropped ~p~n", [Opt]), - activity_ms_and(Head, Opts, Constraints, IDs) - end. - -% Information = information() - -%%% -%%% update_information -%%% - - -update_information(#information{id = Id} = NewInfo) -> - case ets:lookup(pdb_info, Id) of - [] -> - ets:insert(pdb_info, NewInfo), - ok; - [Info] -> - % Remake NewInfo and Info to lists then substitute - % old values for new values that are not undefined or empty lists. - - {_, Result} = lists:foldl( - fun (InfoElem, {[NewInfoElem | Tail], Out}) -> - case NewInfoElem of - undefined -> - {Tail, [InfoElem | Out]}; - [] -> - {Tail, [InfoElem | Out]}; - NewInfoElem -> - {Tail, [NewInfoElem | Out]} - end - end, {tuple_to_list(NewInfo), []}, tuple_to_list(Info)), - ets:insert(pdb_info, list_to_tuple(lists:reverse(Result))), - ok - end. - -update_information_child(Id, Child) -> - case ets:lookup(pdb_info, Id) of - [] -> - ets:insert(pdb_info,#information{ - id = Id, - children = [Child]}), - ok; - [I] -> - ets:insert(pdb_info,I#information{children = [Child | I#information.children]}), - ok - end. - -%%% -%%% update_activity -%%% -update_scheduler(Activity) -> - ets:insert(pdb_scheduler, Activity). - -update_activity(Activity) -> - ets:insert(pdb_activity, Activity). - -%%% -%%% update_system_ts -%%% - -update_system_start_ts(TS) -> - case ets:lookup(pdb_system, {system, start_ts}) of - [] -> - ets:insert(pdb_system, {{system, start_ts}, TS}); - [{{system, start_ts}, StartTS}] -> - DT = ?seconds(StartTS, TS), - if - DT > 0.0 -> ets:insert(pdb_system, {{system, start_ts}, TS}); - true -> ok - end; - Unhandled -> - io:format("update_system_start_ts, unhandled ~p ~n", [Unhandled]) - end. - -update_system_stop_ts(TS) -> - case ets:lookup(pdb_system, {system, stop_ts}) of - [] -> - ets:insert(pdb_system, {{system, stop_ts}, TS}); - [{{system, stop_ts}, StopTS}] -> - DT = ?seconds(StopTS, TS), - if - DT < 0.0 -> ets:insert(pdb_system, {{system, stop_ts}, TS}); - true -> ok - end; - Unhandled -> - io:format("update_system_stop_ts, unhandled ~p ~n", [Unhandled]) - end. diff --git a/lib/percept/src/percept_graph.erl b/lib/percept/src/percept_graph.erl deleted file mode 100644 index e5bbaca2b4..0000000000 --- a/lib/percept/src/percept_graph.erl +++ /dev/null @@ -1,134 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - -%% @doc Interface for CGI request on graphs used by percept. The module exports two functions that are implementations for ESI callbacks used by the httpd server. See http://www.erlang.org//doc/apps/inets/index.html. - --module(percept_graph). --export([proc_lifetime/3, graph/3, scheduler_graph/3, activity/3, percentage/3]). - --include("percept.hrl"). --include_lib("kernel/include/file.hrl"). - -%% API - -%% graph -%% @spec graph(SessionID, Env, Input) -> term() -%% @doc An ESI callback implementation used by the httpd server. -%% - -graph(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, binary_to_list(graph(Env, Input))). - -%% activity -%% @spec activity(SessionID, Env, Input) -> term() -%% @doc An ESI callback implementation used by the httpd server. - -activity(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, binary_to_list(activity_bar(Env, Input))). - -proc_lifetime(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, binary_to_list(proc_lifetime(Env, Input))). - -percentage(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, binary_to_list(percentage(Env,Input))). - -scheduler_graph(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, binary_to_list(scheduler_graph(Env, Input))). - -graph(_Env, Input) -> - Query = httpd:parse_query(Input), - RangeMin = percept_html:get_option_value("range_min", Query), - RangeMax = percept_html:get_option_value("range_max", Query), - Pids = percept_html:get_option_value("pids", Query), - Width = percept_html:get_option_value("width", Query), - Height = percept_html:get_option_value("height", Query), - - % Convert Pids to id option list - IDs = [ {id, ID} || ID <- Pids], - - % seconds2ts - StartTs = percept_db:select({system, start_ts}), - TsMin = percept_analyzer:seconds2ts(RangeMin, StartTs), - TsMax = percept_analyzer:seconds2ts(RangeMax, StartTs), - - Options = [{ts_min, TsMin},{ts_max, TsMax} | IDs], - - Acts = percept_db:select({activity, Options}), - Counts = case IDs of - [] -> percept_analyzer:activities2count(Acts, StartTs); - _ -> percept_analyzer:activities2count2(Acts, StartTs) - end, - - percept_image:graph(Width, Height,Counts). - -scheduler_graph(_Env, Input) -> - Query = httpd:parse_query(Input), - RangeMin = percept_html:get_option_value("range_min", Query), - RangeMax = percept_html:get_option_value("range_max", Query), - Width = percept_html:get_option_value("width", Query), - Height = percept_html:get_option_value("height", Query), - - StartTs = percept_db:select({system, start_ts}), - TsMin = percept_analyzer:seconds2ts(RangeMin, StartTs), - TsMax = percept_analyzer:seconds2ts(RangeMax, StartTs), - - - Acts = percept_db:select({scheduler, [{ts_min, TsMin}, {ts_max,TsMax}]}), - - Counts = [{?seconds(Ts, StartTs), Scheds, 0} || #activity{where = Scheds, timestamp = Ts} <- Acts], - - percept_image:graph(Width, Height, Counts). - -activity_bar(_Env, Input) -> - Query = httpd:parse_query(Input), - Pid = percept_html:get_option_value("pid", Query), - Min = percept_html:get_option_value("range_min", Query), - Max = percept_html:get_option_value("range_max", Query), - Width = percept_html:get_option_value("width", Query), - Height = percept_html:get_option_value("height", Query), - - Data = percept_db:select({activity, [{id, Pid}]}), - StartTs = percept_db:select({system, start_ts}), - Activities = [{?seconds(Ts, StartTs), State} || #activity{timestamp = Ts, state = State} <- Data], - - percept_image:activities(Width, Height, {Min,Max},Activities). - -proc_lifetime(_Env, Input) -> - Query = httpd:parse_query(Input), - ProfileTime = percept_html:get_option_value("profiletime", Query), - Start = percept_html:get_option_value("start", Query), - End = percept_html:get_option_value("end", Query), - Width = percept_html:get_option_value("width", Query), - Height = percept_html:get_option_value("height", Query), - percept_image:proc_lifetime(round(Width), round(Height), float(Start), float(End), float(ProfileTime)). - -percentage(_Env, Input) -> - Query = httpd:parse_query(Input), - Width = percept_html:get_option_value("width", Query), - Height = percept_html:get_option_value("height", Query), - Percentage = percept_html:get_option_value("percentage", Query), - percept_image:percentage(round(Width), round(Height), float(Percentage)). - -header() -> - "Content-Type: image/png\r\n\r\n". diff --git a/lib/percept/src/percept_html.erl b/lib/percept/src/percept_html.erl deleted file mode 100644 index a675227584..0000000000 --- a/lib/percept/src/percept_html.erl +++ /dev/null @@ -1,707 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - --module(percept_html). --export([page/3, - codelocation_page/3, - databases_page/3, - load_database_page/3, - processes_page/3, - concurrency_page/3, - process_info_page/3]). - --export([value2pid/1, - pid2value/1, - get_option_value/2, - join_strings_with/2]). - --include("percept.hrl"). --include_lib("kernel/include/file.hrl"). - - -%% API - -page(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, menu()), - ok = mod_esi:deliver(SessionID, overview_content(Env, Input)), - ok = mod_esi:deliver(SessionID, footer()). - -processes_page(SessionID, _, _) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, menu()), - ok = mod_esi:deliver(SessionID, processes_content()), - ok = mod_esi:deliver(SessionID, footer()). - -concurrency_page(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, menu()), - ok = mod_esi:deliver(SessionID, concurrency_content(Env, Input)), - ok = mod_esi:deliver(SessionID, footer()). - -databases_page(SessionID, _, _) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, menu()), - ok = mod_esi:deliver(SessionID, databases_content()), - ok = mod_esi:deliver(SessionID, footer()). - -codelocation_page(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, menu()), - ok = mod_esi:deliver(SessionID, codelocation_content(Env, Input)), - ok = mod_esi:deliver(SessionID, footer()). - -process_info_page(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - ok = mod_esi:deliver(SessionID, menu()), - ok = mod_esi:deliver(SessionID, process_info_content(Env, Input)), - ok = mod_esi:deliver(SessionID, footer()). - -load_database_page(SessionID, Env, Input) -> - ok = mod_esi:deliver(SessionID, header()), - - % Very dynamic page, handled differently - load_database_content(SessionID, Env, Input), - ok = mod_esi:deliver(SessionID, footer()). - - -%%% --------------------------- %%% -%%% Content pages %%% -%%% --------------------------- %%% - -overview_content(_Env, Input) -> - Query = httpd:parse_query(Input), - Min = get_option_value("range_min", Query), - Max = get_option_value("range_max", Query), - Width = 1200, - Height = 600, - TotalProfileTime = ?seconds( percept_db:select({system, stop_ts}), - percept_db:select({system, start_ts})), - RegisteredProcs = length(percept_db:select({information, procs})), - RegisteredPorts = length(percept_db:select({information, ports})), - - InformationTable = - "<table>" ++ - table_line(["Profile time:", TotalProfileTime]) ++ - table_line(["Processes:", RegisteredProcs]) ++ - table_line(["Ports:", RegisteredPorts]) ++ - table_line(["Min. range:", Min]) ++ - table_line(["Max. range:", Max]) ++ - "</table>", - - Header = " - <div id=\"content\"> - <div>" ++ InformationTable ++ "</div>\n - <form name=form_area method=POST action=/cgi-bin/percept_html/page> - <input name=data_min type=hidden value=" ++ term2html(float(Min)) ++ "> - <input name=data_max type=hidden value=" ++ term2html(float(Max)) ++ ">\n", - - - RangeTable = - "<table>"++ - table_line([ - "Min:", - "<input name=range_min value=" ++ term2html(float(Min)) ++">", - "<select name=\"graph_select\" onChange=\"select_image()\"> - <option disabled=true value=\""++ url_graph(Width, Height, Min, Max, []) ++"\" />Ports - <option disabled=true value=\""++ url_graph(Width, Height, Min, Max, []) ++"\" />Processes - <option value=\""++ url_graph(Width, Height, Min, Max, []) ++"\" />Ports & Processes - </select>", - "<input type=submit value=Update>" - ]) ++ - table_line([ - "Max:", - "<input name=range_max value=" ++ term2html(float(Max)) ++">", - "", - "<a href=/cgi-bin/percept_html/codelocation_page?range_min=" ++ - term2html(Min) ++ "&range_max=" ++ term2html(Max) ++ ">Code location</a>" - ]) ++ - "</table>", - - - MainTable = - "<table>" ++ - table_line([div_tag_graph()]) ++ - table_line([RangeTable]) ++ - "</table>", - - Footer = "</div></form>", - - Header ++ MainTable ++ Footer. - -div_tag_graph() -> - %background:url('/images/loader.gif') no-repeat center; - "<div id=\"percept_graph\" - onMouseDown=\"select_down(event)\" - onMouseMove=\"select_move(event)\" - onMouseUp=\"select_up(event)\" - - style=\" - background-size: 100%; - background-origin: content; - width: 100%; - position:relative; - \"> - - <div id=\"percept_areaselect\" - style=\"background-color:#ef0909; - position:relative; - visibility:hidden; - border-left: 1px solid #101010; - border-right: 1px solid #101010; - z-index:2; - width:40px; - height:40px;\"></div></div>". - --spec url_graph( - Widht :: non_neg_integer(), - Height :: non_neg_integer(), - Min :: float(), - Max :: float(), - Pids :: [pid()]) -> string(). - -url_graph(W, H, Min, Max, []) -> - "/cgi-bin/percept_graph/graph?range_min=" ++ term2html(float(Min)) - ++ "&range_max=" ++ term2html(float(Max)) - ++ "&width=" ++ term2html(float(W)) - ++ "&height=" ++ term2html(float(H)). - -%%% process_info_content - -process_info_content(_Env, Input) -> - Query = httpd:parse_query(Input), - Pid = get_option_value("pid", Query), - - - [I] = percept_db:select({information, Pid}), - ArgumentString = case I#information.entry of - {_, _, Arguments} -> lists:flatten( [term2html(Arg) ++ "<br>" || Arg <- Arguments]); - _ -> "" - end, - - TimeTable = html_table([ - [{th, ""}, - {th, "Timestamp"}, - {th, "Profile Time"}], - [{td, "Start"}, - term2html(I#information.start), - term2html(procstarttime(I#information.start))], - [{td, "Stop"}, - term2html(I#information.stop), - term2html(procstoptime(I#information.stop))] - ]), - - InfoTable = html_table([ - [{th, "Pid"}, term2html(I#information.id)], - [{th, "Name"}, term2html(I#information.name)], - [{th, "Entrypoint"}, mfa2html(I#information.entry)], - [{th, "Arguments"}, ArgumentString], - [{th, "Timetable"}, TimeTable], - [{th, "Parent"}, pid2html(I#information.parent)], - [{th, "Children"}, lists:flatten(lists:map(fun(Child) -> pid2html(Child) ++ " " end, I#information.children))] - ]), - - PidActivities = percept_db:select({activity, [{id, Pid}]}), - WaitingMfas = percept_analyzer:waiting_activities(PidActivities), - - TotalWaitTime = lists:sum( [T || {T, _, _} <- WaitingMfas] ), - - MfaTable = html_table([ - [{th, "percentage"}, - {th, "total"}, - {th, "mean"}, - {th, "stddev"}, - {th, "#recv"}, - {th, "module:function/arity"}]] ++ [ - [{td, image_string(percentage, [{width, 100}, {height, 10}, {percentage, Time/TotalWaitTime}])}, - {td, term2html(Time)}, - {td, term2html(Mean)}, - {td, term2html(StdDev)}, - {td, term2html(N)}, - {td, mfa2html(MFA)} ] || {Time, MFA, {Mean, StdDev, N}} <- WaitingMfas]), - - "<div id=\"content\">" ++ - InfoTable ++ "<br>" ++ - MfaTable ++ - "</div>". - -%%% concurrency content -concurrency_content(_Env, Input) -> - %% Get query - Query = httpd:parse_query(Input), - - %% Collect selected pids and generate id tags - Pids = [value2pid(PidValue) || {PidValue, Case} <- Query, Case == "on", PidValue /= "select_all"], - IDs = [{id, Pid} || Pid <- Pids], - - % FIXME: A lot of extra work here, redo - - %% Analyze activities and calculate area bounds - Activities = percept_db:select({activity, IDs}), - StartTs = percept_db:select({system, start_ts}), - Counts = [{Time, Y1 + Y2} || {Time, Y1, Y2} <- percept_analyzer:activities2count2(Activities, StartTs)], - {T0,_,T1,_} = percept_analyzer:minmax(Counts), - - % FIXME: End - - PidValues = [pid2value(Pid) || Pid <- Pids], - - %% Generate activity bar requests - ActivityBarTable = lists:foldl( - fun(Pid, Out) -> - ValueString = pid2value(Pid), - Out ++ - table_line([ - pid2html(Pid), - "<img onload=\"size_image(this, '" ++ - image_string_head("activity", [{"pid", ValueString}, {range_min, T0},{range_max, T1},{height, 10}], []) ++ - "')\" src=/images/white.png border=0 />" - ]) - end, [], Pids), - - %% Make pids request string - PidsRequest = join_strings_with(PidValues, ":"), - - "<div id=\"content\"> - <table cellspacing=0 cellpadding=0 border=0>" ++ - table_line([ - "", - "<img onload=\"size_image(this, '" ++ - image_string_head("graph", [{"pids", PidsRequest},{range_min, T0}, {range_max, T1}, {height, 400}], []) ++ - "')\" src=/images/white.png border=0 />" - ]) ++ - ActivityBarTable ++ - "</table></div>\n". - -processes_content() -> - Ports = percept_db:select({information, ports}), - UnsortedProcesses = percept_db:select({information, procs}), - SystemStartTS = percept_db:select({system, start_ts}), - SystemStopTS = percept_db:select({system, stop_ts}), - ProfileTime = ?seconds( SystemStopTS, - SystemStartTS), - Processes = lists:sort( - fun (A, B) -> - if - A#information.id > B#information.id -> true; - true -> false - end - end, UnsortedProcesses), - - ProcsHtml = lists:foldl( - fun (I, Out) -> - StartTime = procstarttime(I#information.start), - EndTime = procstoptime(I#information.stop), - Prepare = - table_line([ - "<input type=checkbox name=" ++ pid2value(I#information.id) ++ ">", - pid2html(I#information.id), - image_string(proc_lifetime, [ - {profiletime, ProfileTime}, - {start, StartTime}, - {"end", term2html(float(EndTime))}, - {width, 100}, - {height, 10}]), - mfa2html(I#information.entry), - term2html(I#information.name), - pid2html(I#information.parent) - ]), - [Prepare|Out] - end, [], Processes), - - PortsHtml = lists:foldl( - fun (I, Out) -> - StartTime = procstarttime(I#information.start), - EndTime = procstoptime(I#information.stop), - Prepare = - table_line([ - "", - pid2html(I#information.id), - image_string(proc_lifetime, [ - {profiletime, ProfileTime}, - {start, StartTime}, - {"end", term2html(float(EndTime))}, - {width, 100}, - {height, 10}]), - mfa2html(I#information.entry), - term2html(I#information.name), - pid2html(I#information.parent) - ]), - [Prepare|Out] - end, [], Ports), - - Selector = "<table>" ++ - table_line([ - "<input onClick='selectall()' type=checkbox name=select_all>Select all"]) ++ - table_line([ - "<input type=submit value=Compare>"]) ++ - "</table>", - - if - length(ProcsHtml) > 0 -> - ProcsHtmlResult = - "<tr><td><b>Processes</b></td></tr> - <tr><td> - <table width=700 cellspacing=0 border=0> - <tr> - <td align=middle width=40><b>Select</b></td> - <td align=middle width=40><b>Pid</b></td> - <td><b>Lifetime</b></td> - <td><b>Entrypoint</b></td> - <td><b>Name</b></td> - <td><b>Parent</b></td> - </tr>" ++ - lists:flatten(ProcsHtml) ++ - "</table> - </td></tr>"; - true -> - ProcsHtmlResult = "" - end, - if - length(PortsHtml) > 0 -> - PortsHtmlResult = " - <tr><td><b>Ports</b></td></tr> - <tr><td> - <table width=700 cellspacing=0 border=0> - <tr> - <td align=middle width=40><b>Select</b></td> - <td align=left width=40><b>Pid</b></td> - <td><b>Lifetime</b></td> - <td><b>Entrypoint</b></td> - <td><b>Name</b></td> - <td><b>Parent</b></td> - </tr>" ++ - lists:flatten(PortsHtml) ++ - "</table> - </td></tr>"; - true -> - PortsHtmlResult = "" - end, - - Right = "<div>" - ++ Selector ++ - "</div>\n", - - Middle = "<div id=\"content\"> - <table>" ++ - ProcsHtmlResult ++ - PortsHtmlResult ++ - "</table>" ++ - Right ++ - "</div>\n", - - "<form name=process_select method=POST action=/cgi-bin/percept_html/concurrency_page>" ++ - Middle ++ - "</form>". - -procstarttime(TS) -> - case TS of - undefined -> 0.0; - TS -> ?seconds(TS,percept_db:select({system, start_ts})) - end. - -procstoptime(TS) -> - case TS of - undefined -> ?seconds( percept_db:select({system, stop_ts}), - percept_db:select({system, start_ts})); - TS -> ?seconds(TS, percept_db:select({system, start_ts})) - end. - -databases_content() -> - "<div id=\"content\"> - <form name=load_percept_file method=post action=/cgi-bin/percept_html/load_database_page> - <center> - <table> - <tr><td>Enter file to analyse:</td><td><input type=hidden name=path /></td></tr> - <tr><td><input type=file name=file size=40 /></td><td><input type=submit value=Load onClick=\"path.value = file.value;\" /></td></tr> - </table> - </center> - </form> - </div>". - -load_database_content(SessionId, _Env, Input) -> - Query = httpd:parse_query(Input), - {_,{_,Path}} = lists:keysearch("file", 1, Query), - {_,{_,File}} = lists:keysearch("path", 1, Query), - Filename = filename:join(Path, File), - % Check path/file/filename - - ok = mod_esi:deliver(SessionId, "<div id=\"content\">"), - case file:read_file_info(Filename) of - {ok, _} -> - Content = "<center> - Parsing: " ++ Filename ++ "<br> - </center>", - ok = mod_esi:deliver(SessionId, Content), - case percept:analyze(Filename) of - {error, Reason} -> - ok = mod_esi:deliver(SessionId, error_msg("Analyze" ++ term2html(Reason))); - _ -> - Complete = "<center><a href=\"/cgi-bin/percept_html/page\">View</a></center>", - ok = mod_esi:deliver(SessionId, Complete) - end; - {error, Reason} -> - ok = mod_esi:deliver(SessionId, error_msg("File" ++ term2html(Reason))) - end, - ok = mod_esi:deliver(SessionId, "</div>"). - -codelocation_content(_Env, Input) -> - Query = httpd:parse_query(Input), - Min = get_option_value("range_min", Query), - Max = get_option_value("range_max", Query), - StartTs = percept_db:select({system, start_ts}), - TsMin = percept_analyzer:seconds2ts(Min, StartTs), - TsMax = percept_analyzer:seconds2ts(Max, StartTs), - Acts = percept_db:select({activity, [{ts_min, TsMin}, {ts_max, TsMax}]}), - - Secs = [timer:now_diff(A#activity.timestamp,StartTs)/1000 || A <- Acts], - Delta = cl_deltas(Secs), - Zip = lists:zip(Acts, Delta), - Table = html_table([ - [{th, "delta [ms]"}, - {th, "time [ms]"}, - {th, " pid "}, - {th, "activity"}, - {th, "module:function/arity"}, - {th, "#runnables"}]] ++ [ - [{td, term2html(D)}, - {td, term2html(timer:now_diff(A#activity.timestamp,StartTs)/1000)}, - {td, pid2html(A#activity.id)}, - {td, term2html(A#activity.state)}, - {td, mfa2html(A#activity.where)}, - {td, term2html(A#activity.runnable_count)}] || {A, D} <- Zip ]), - - "<div id=\"content\">" ++ - Table ++ - "</div>". - -cl_deltas([]) -> []; -cl_deltas(List) -> cl_deltas(List, [0.0]). -cl_deltas([_], Out) -> lists:reverse(Out); -cl_deltas([A,B|Ls], Out) -> cl_deltas([B|Ls], [B - A | Out]). - -%%% --------------------------- %%% -%%% Utility functions %%% -%%% --------------------------- %%% - -%% Should be in string stdlib? - -join_strings(Strings) -> - lists:flatten(Strings). - --spec join_strings_with(Strings :: [string()], Separator :: string()) -> string(). - -join_strings_with([S1, S2 | R], S) -> - join_strings_with([join_strings_with(S1,S2,S) | R], S); -join_strings_with([S], _) -> - S. -join_strings_with(S1, S2, S) -> - join_strings([S1,S,S2]). - -%%% Generic erlang2html - --spec html_table(Rows :: [[string() | {'td' | 'th', string()}]]) -> string(). - -html_table(Rows) -> "<table>" ++ html_table_row(Rows) ++ "</table>". - -html_table_row(Rows) -> html_table_row(Rows, odd). -html_table_row([], _) -> ""; -html_table_row([Row|Rows], odd ) -> "<tr class=\"odd\">" ++ html_table_data(Row) ++ "</tr>" ++ html_table_row(Rows, even); -html_table_row([Row|Rows], even) -> "<tr class=\"even\">" ++ html_table_data(Row) ++ "</tr>" ++ html_table_row(Rows, odd ). - -html_table_data([]) -> ""; -html_table_data([{td, Data}|Row]) -> "<td>" ++ Data ++ "</td>" ++ html_table_data(Row); -html_table_data([{th, Data}|Row]) -> "<th>" ++ Data ++ "</th>" ++ html_table_data(Row); -html_table_data([Data|Row]) -> "<td>" ++ Data ++ "</td>" ++ html_table_data(Row). - - - - --spec table_line(Table :: [any()]) -> string(). - -table_line(List) -> table_line(List, ["<tr>"]). -table_line([], Out) -> lists:flatten(lists:reverse(["</tr>\n"|Out])); -table_line([Element | Elements], Out) when is_list(Element) -> - table_line(Elements, ["<td>" ++ Element ++ "</td>" |Out]); -table_line([Element | Elements], Out) -> - table_line(Elements, ["<td>" ++ term2html(Element) ++ "</td>"|Out]). - --spec term2html(any()) -> string(). - -term2html(Term) when is_float(Term) -> lists:flatten(io_lib:format("~.4f", [Term])); -term2html(Term) -> lists:flatten(io_lib:format("~p", [Term])). - --spec mfa2html(MFA :: {atom(), atom(), list() | integer()}) -> string(). - -mfa2html({Module, Function, Arguments}) when is_list(Arguments) -> - lists:flatten(io_lib:format("~p:~p/~p", [Module, Function, length(Arguments)])); -mfa2html({Module, Function, Arity}) when is_integer(Arity) -> - lists:flatten(io_lib:format("~p:~p/~p", [Module, Function, Arity])); -mfa2html(_) -> - "undefined". - --spec pid2html(Pid :: pid() | port()) -> string(). - -pid2html(Pid) when is_pid(Pid) -> - PidString = term2html(Pid), - PidValue = pid2value(Pid), - "<a href=\"/cgi-bin/percept_html/process_info_page?pid="++PidValue++"\">"++PidString++"</a>"; -pid2html(Pid) when is_port(Pid) -> - term2html(Pid); -pid2html(_) -> - "undefined". - --spec image_string(Request :: string()) -> string(). - -image_string(Request) -> - "<img border=0 src=\"/cgi-bin/percept_graph/" ++ - Request ++ - " \">". - --spec image_string(atom() | string(), list()) -> string(). - -image_string(Request, Options) when is_atom(Request), is_list(Options) -> - image_string(image_string_head(erlang:atom_to_list(Request), Options, [])); -image_string(Request, Options) when is_list(Options) -> - image_string(image_string_head(Request, Options, [])). - -image_string_head(Request, [{Type, Value} | Opts], Out) when is_atom(Type), is_number(Value) -> - Opt = join_strings(["?",term2html(Type),"=",term2html(Value)]), - image_string_tail(Request, Opts, [Opt|Out]); -image_string_head(Request, [{Type, Value} | Opts], Out) -> - Opt = join_strings(["?",Type,"=",Value]), - image_string_tail(Request, Opts, [Opt|Out]). - -image_string_tail(Request, [], Out) -> - join_strings([Request | lists:reverse(Out)]); -image_string_tail(Request, [{Type, Value} | Opts], Out) when is_atom(Type), is_number(Value) -> - Opt = join_strings(["&",term2html(Type),"=",term2html(Value)]), - image_string_tail(Request, Opts, [Opt|Out]); -image_string_tail(Request, [{Type, Value} | Opts], Out) -> - Opt = join_strings(["&",Type,"=",Value]), - image_string_tail(Request, Opts, [Opt|Out]). - - -%%% percept conversions - --spec pid2value(Pid :: pid()) -> string(). - -pid2value(Pid) -> - String = lists:flatten(io_lib:format("~p", [Pid])), - lists:sublist(String, 2, erlang:length(String)-2). - --spec value2pid(Value :: string()) -> pid(). - -value2pid(Value) -> - String = lists:flatten("<" ++ Value ++ ">"), - erlang:list_to_pid(String). - - -%%% get value - --spec get_option_value(Option :: string(), Options :: [{string(),any()}]) -> - {'error', any()} | boolean() | pid() | [pid()] | number(). - -get_option_value(Option, Options) -> - case catch get_option_value0(Option, Options) of - {'EXIT', Reason} -> {error, Reason}; - Value -> Value - end. - -get_option_value0(Option, Options) -> - case lists:keysearch(Option, 1, Options) of - false -> get_default_option_value(Option); - {value, {Option, _Value}} when Option == "fillcolor" -> true; - {value, {Option, Value}} when Option == "pid" -> value2pid(Value); - {value, {Option, Value}} when Option == "pids" -> - [value2pid(PidValue) || PidValue <- string:tokens(Value,":")]; - {value, {Option, Value}} -> get_number_value(Value); - _ -> {error, undefined} - end. - -get_default_option_value(Option) -> - case Option of - "fillcolor" -> false; - "range_min" -> float(0.0); - "pids" -> []; - "range_max" -> - Acts = percept_db:select({activity, []}), - #activity{ timestamp = Start } = hd(Acts), - #activity{ timestamp = Stop } = hd(lists:reverse(Acts)), - ?seconds(Stop,Start); - "width" -> 700; - "height" -> 400; - _ -> {error, {undefined_default_option, Option}} - end. - --spec get_number_value(string()) -> number() | {'error', 'illegal_number'}. - -get_number_value(Value) -> - % Try float - case string:to_float(Value) of - {error, no_float} -> - % Try integer - case string:to_integer(Value) of - {error, _} -> {error, illegal_number}; - {Integer, _} -> Integer - end; - {error, _} -> {error, illegal_number}; - {Float, _} -> Float - end. - -%%% --------------------------- %%% -%%% html prime functions %%% -%%% --------------------------- %%% - -header() -> header([]). -header(HeaderData) -> - "Content-Type: text/html\r\n\r\n" ++ - "<html> - <head> - <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"> - <title>percept</title> - <link href=\"/css/percept.css\" rel=\"stylesheet\" type=\"text/css\"> - <script type=\"text/javascript\" src=\"/javascript/percept_error_handler.js\"></script> - <script type=\"text/javascript\" src=\"/javascript/percept_select_all.js\"></script> - <script type=\"text/javascript\" src=\"/javascript/percept_area_select.js\"></script> - " ++ HeaderData ++" - </head> - <body onLoad=\"load_image()\"> - <div id=\"header\"><a href=/index.html>percept</a></div>\n". - -footer() -> - "</body> - </html>\n". - -menu() -> - "<div id=\"menu\" class=\"menu_tabs\"> - <ul> - <li><a href=/cgi-bin/percept_html/databases_page>databases</a></li> - <li><a href=/cgi-bin/percept_html/processes_page>processes</a></li> - <li><a href=/cgi-bin/percept_html/page>overview</a></li> - </ul></div>\n". - --spec error_msg(Error :: string()) -> string(). - -error_msg(Error) -> - "<table width=300> - <tr height=5><td></td> <td></td></tr> - <tr><td width=150 align=right><b>Error: </b></td> <td align=left>"++ Error ++ "</td></tr> - <tr height=5><td></td> <td></td></tr> - </table>\n". diff --git a/lib/percept/src/percept_image.erl b/lib/percept/src/percept_image.erl deleted file mode 100644 index e819938027..0000000000 --- a/lib/percept/src/percept_image.erl +++ /dev/null @@ -1,316 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% - --module(percept_image). --export([ proc_lifetime/5, - percentage/3, - graph/3, - graph/4, - activities/3, - activities/4]). --record(graph_area, {x = 0, y = 0, width, height}). --compile(inline). - -%%% ------------------------------------- -%%% GRAF -%%% ------------------------------------- - -%% graph(Widht, Height, Range, Data) - -graph(Width, Height, {RXmin, RYmin, RXmax, RYmax}, Data) -> - Data2 = [{X, Y1 + Y2} || {X, Y1, Y2} <- Data], - MinMax = percept_analyzer:minmax(Data2), - {Xmin, Ymin, Xmax, Ymax} = MinMax, - graf1(Width, Height,{ lists:min([RXmin, Xmin]), - lists:min([RYmin, Ymin]), - lists:max([RXmax, Xmax]), - lists:max([RYmax, Ymax])}, Data). - -%% graph(Widht, Height, Data) = Image -%% In: -%% Width = integer(), -%% Height = integer(), -%% Data = [{Time, Procs, Ports}] -%% Time = float() -%% Procs = integer() -%% Ports = integer() -%% Out: -%% Image = binary() - -graph(Width, Height, Data) -> - Data2 = [{X, Y1 + Y2} || {X, Y1, Y2} <- Data], - Bounds = percept_analyzer:minmax(Data2), - graf1(Width, Height, Bounds, Data). - -graf1(Width, Height, {Xmin, Ymin, Xmax, Ymax}, Data) -> - % Calculate areas - HO = 20, - GrafArea = #graph_area{x = HO, y = 4, width = Width - 2*HO, height = Height - 17}, - XticksArea = #graph_area{x = HO, y = Height - 13, width = Width - 2*HO, height = 13}, - YticksArea = #graph_area{x = 1, y = 4, width = HO, height = Height - 17}, - - %% Initiate Image - - Image = egd:create(Width, Height), - - %% Set colors - - Black = egd:color(Image, {0, 0, 0}), - ProcColor = egd:color(Image, {0, 255, 0}), - PortColor = egd:color(Image, {255, 0, 0}), - - %% Draw graf, xticks and yticks - draw_graf(Image, Data, {Black, ProcColor, PortColor}, GrafArea, {Xmin, Ymin, Xmax, Ymax}), - draw_xticks(Image, Black, XticksArea, {Xmin, Xmax}, Data), - draw_yticks(Image, Black, YticksArea, {Ymin, Ymax}), - - %% Kill image and return binaries - Binary = egd:render(Image, png), - egd:destroy(Image), - Binary. - -%% draw_graf(Image, Data, Color, GraphArea, DataBounds) -%% Image, port to Image -%% Data, list of three tuple data, (X, Y1, Y2) -%% Color, {ForegroundColor, ProcFillColor, PortFillColor} -%% DataBounds, {Xmin, Ymin, Xmax, Ymax} - -draw_graf(Im, Data, Colors, GA = #graph_area{x = X0, y = Y0, width = Width, height = Height}, {Xmin, _Ymin, Xmax, Ymax}) -> - Dx = (Width)/(Xmax - Xmin), - Dy = (Height)/(Ymax), - Plotdata = [{trunc(X0 + X*Dx - Xmin*Dx), trunc(Y0 + Height - Y1*Dy), trunc(Y0 + Height - (Y1 + Y2)*Dy)} || {X, Y1, Y2} <- Data], - draw_graf(Im, Plotdata, Colors, GA). - -draw_graf(Im, [{X1, Yproc1, Yport1}, {X2, Yproc2, Yport2}|Data], C, GA) when X2 - X1 < 1 -> - draw_graf(Im, [{X1, [{Yproc2, Yport2},{Yproc1, Yport1}]}|Data], C, GA); - -draw_graf(Im, [{X1, Ys1}, {X2, Yproc2, Yport2}|Data], C, GA) when X2 - X1 < 1, is_list(Ys1) -> - draw_graf(Im, [{X1, [{Yproc2, Yport2}|Ys1]}|Data], C, GA); - -draw_graf(Im, [{X1, Yproc1, Yport1}, {X2, Yproc2, Yport2}|Data], C = {B, PrC, PoC}, GA = #graph_area{y = Y0, height = H}) -> - GyZero = trunc(Y0 + H), - egd:filledRectangle(Im, {X1, GyZero}, {X2, Yproc1}, PrC), - egd:filledRectangle(Im, {X1, Yproc1}, {X2, Yport1}, PoC), - egd:line(Im, {X1, Yport1}, {X2, Yport1}, B), % top line - egd:line(Im, {X1, Yport2}, {X1, Yport1}, B), % right line - egd:line(Im, {X2, Yport1}, {X2, Yport2}, B), % right line - draw_graf(Im, [{X2, Yproc2, Yport2}|Data], C, GA); - -draw_graf(Im, [{X1, Ys1 = [{Yproc1,Yport1}|_]}, {X2, Yproc2, Yport2}|Data], C = {B, PrC, PoC}, GA = #graph_area{y = Y0, height = H}) -> - GyZero = trunc(Y0 + H), - Yprocs = [Yp || {Yp, _} <- Ys1], - Yports = [Yp || {_, Yp} <- Ys1], - - YprMin = lists:min(Yprocs), - YprMax = lists:max(Yprocs), - YpoMax = lists:max(Yports), - egd:filledRectangle(Im, {X1, GyZero}, {X2, Yproc1}, PrC), - egd:filledRectangle(Im, {X1, Yproc1}, {X2, Yport1}, PoC), - egd:filledRectangle(Im, {X1, Yport1}, {X2, Yport1}, B), % top line - egd:filledRectangle(Im, {X2, Yport1}, {X2, Yport2}, B), % right line - - egd:filledRectangle(Im, {X1, GyZero}, {X1, YprMin}, PrC), % left proc green line - egd:filledRectangle(Im, {X1, YprMax}, {X1, YpoMax}, PoC), % left port line - egd:filledRectangle(Im, {X1, YprMax}, {X1, YprMin}, B), - - draw_graf(Im, [{X2, Yproc2, Yport2}|Data], C, GA); -draw_graf(_, _, _, _) -> ok. - -draw_xticks(Image, Color, XticksArea, {Xmin, Xmax}, Data) -> - #graph_area{x = X0, y = Y0, width = Width} = XticksArea, - - DX = Width/(Xmax - Xmin), - Offset = X0 - Xmin*DX, - Y = trunc(Y0), - Font = load_font(), - {FontW, _FontH} = egd_font:size(Font), - egd:filledRectangle(Image, {trunc(X0), Y}, {trunc(X0 + Width), Y}, Color), - lists:foldl( - fun ({X,_,_}, PX) -> - X1 = trunc(Offset + X*DX), - - % Optimization: - % if offset has past half the previous text - % start checking this text - - if - X1 > PX -> - Text = lists:flatten(io_lib:format("~.3f", [float(X)])), - TextLength = length(Text), - TextWidth = TextLength*FontW, - Spacing = 2, - if - X1 > PX + round(TextWidth/2) + Spacing -> - egd:line(Image, {X1, Y - 3}, {X1, Y + 3}, Color), - text(Image, {X1 - round(TextWidth/2), Y + 2}, Font, Text, Color), - X1 + round(TextWidth/2) + Spacing; - true -> - PX - end; - true -> - PX - end - end, 0, Data). - -draw_yticks(Im, Color, TickArea, {_,Ymax}) -> - #graph_area{x = X0, y = Y0, width = Width, height = Height} = TickArea, - Font = load_font(), - X = trunc(X0 + Width), - Dy = (Height)/(Ymax), - Yts = if - Height/(Ymax*12) < 1.0 -> round(1 + Ymax*15/Height); - true -> 1 - end, - egd:filledRectangle(Im, {X, trunc(0 + Y0)}, {X, trunc(Y0 + Height)}, Color), - draw_yticks0(Im, Font, Color, 0, Yts, Ymax, {X, Height, Dy}). - -draw_yticks0(Im, Font, Color, Yi, Yts, Ymax, Area) when Yi < Ymax -> - {X, Height, Dy} = Area, - Y = round(Height - (Yi*Dy) + 3), - - egd:filledRectangle(Im, {X - 3, Y}, {X + 3, Y}, Color), - Text = lists:flatten(io_lib:format("~p", [Yi])), - text(Im, {0, Y - 4}, Font, Text, Color), - draw_yticks0(Im, Font, Color, Yi + Yts, Yts, Ymax, Area); -draw_yticks0(_, _, _, _, _, _, _) -> ok. - -%%% ------------------------------------- -%%% ACTIVITIES -%%% ------------------------------------- - -%% activities(Width, Height, Range, Activities) -> Binary -%% In: -%% Width = integer() -%% Height = integer() -%% Range = {float(), float()} -%% Activities = [{float(), active | inactive}] -%% Out: -%% Binary = binary() - -activities(Width, Height, {UXmin, UXmax}, Activities) -> - Xs = [ X || {X,_} <- Activities], - Xmin = lists:min(Xs), - Xmax = lists:max(Xs), - activities0(Width, Height, {lists:min([Xmin, UXmin]), lists:max([UXmax, Xmax])}, Activities). - -activities(Width, Height, Activities) -> - Xs = [ X || {X,_} <- Activities], - Xmin = lists:min(Xs), - Xmax = lists:max(Xs), - activities0(Width, Height, {Xmin, Xmax}, Activities). - -activities0(Width, Height, {Xmin, Xmax}, Activities) -> - Image = egd:create(Width, Height), - Grey = egd:color(Image, {200, 200, 200}), - HO = 20, - ActivityArea = #graph_area{x = HO, y = 0, width = Width - 2*HO, height = Height}, - egd:filledRectangle(Image, {0, 0}, {Width, Height}, Grey), - draw_activity(Image, {Xmin, Xmax}, ActivityArea, Activities), - Binary = egd:render(Image, png), - egd:destroy(Image), - Binary. - -draw_activity(Image, {Xmin, Xmax}, Area = #graph_area{ width = Width }, Acts) -> - White = egd:color({255, 255, 255}), - Green = egd:color({0,250, 0}), - Black = egd:color({0, 0, 0}), - - Dx = Width/(Xmax - Xmin), - - draw_activity(Image, {Xmin, Xmax}, Area, {White, Green, Black}, Dx, Acts). - -draw_activity(_, _, _, _, _, [_]) -> ok; -draw_activity(Image, {Xmin, Xmax}, Area = #graph_area{ height = Height, x = X0 }, {Cw, Cg, Cb}, Dx, [{Xa1, State}, {Xa2, Act2} | Acts]) -> - X1 = erlang:trunc(X0 + Dx*Xa1 - Xmin*Dx), - X2 = erlang:trunc(X0 + Dx*Xa2 - Xmin*Dx), - - case State of - inactive -> - egd:filledRectangle(Image, {X1, 0}, {X2, Height - 1}, Cw), - egd:rectangle(Image, {X1, 0}, {X2, Height - 1}, Cb); - active -> - egd:filledRectangle(Image, {X1, 0}, {X2, Height - 1}, Cg), - egd:rectangle(Image, {X1, 0}, {X2, Height - 1}, Cb) - end, - draw_activity(Image, {Xmin, Xmax}, Area, {Cw, Cg, Cb}, Dx, [{Xa2, Act2} | Acts]). - - - -%%% ------------------------------------- -%%% Process lifetime -%%% Used by processes page -%%% ------------------------------------- - -proc_lifetime(Width, Height, Start, End, ProfileTime) -> - Im = egd:create(round(Width), round(Height)), - Black = egd:color(Im, {0, 0, 0}), - Green = egd:color(Im, {0, 255, 0}), - - % Ratio and coordinates - - DX = (Width-1)/ProfileTime, - X1 = round(DX*Start), - X2 = round(DX*End), - - % Paint - egd:filledRectangle(Im, {X1, 0}, {X2, Height - 1}, Green), - egd:rectangle(Im, {X1, 0}, {X2, Height - 1}, Black), - - Binary = egd:render(Im, png), - egd:destroy(Im), - Binary. - -%%% ------------------------------------- -%%% Percentage -%%% Used by process_info page -%%% Percentage should be 0.0 -> 1.0 -%%% ------------------------------------- -percentage(Width, Height, Percentage) -> - Im = egd:create(round(Width), round(Height)), - Font = load_font(), - Black = egd:color(Im, {0, 0, 0}), - Green = egd:color(Im, {0, 255, 0}), - - % Ratio and coordinates - - X = round(Width - 1 - Percentage*(Width - 1)), - - % Paint - egd:filledRectangle(Im, {X, 0}, {Width - 1, Height - 1}, Green), - {FontW, _} = egd_font:size(Font), - String = lists:flatten(io_lib:format("~.10B %", [round(100*Percentage)])), - - text( Im, - {round(Width/2 - (FontW*length(String)/2)), 0}, - Font, - String, - Black), - egd:rectangle(Im, {X, 0}, {Width - 1, Height - 1}, Black), - - Binary = egd:render(Im, png), - egd:destroy(Im), - Binary. - - -load_font() -> - Filename = filename:join([code:priv_dir(percept),"fonts", "6x11_latin1.wingsfont"]), - egd_font:load(Filename). - -text(Image, {X,Y}, Font, Text, Color) -> - egd:text(Image, {X,Y-2}, Font, Text, Color). diff --git a/lib/percept/test/Makefile b/lib/percept/test/Makefile deleted file mode 100644 index 87fde49410..0000000000 --- a/lib/percept/test/Makefile +++ /dev/null @@ -1,91 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2007-2016. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# %CopyrightEnd% -# - -include $(ERL_TOP)/make/target.mk - -include $(ERL_TOP)/make/$(TARGET)/otp.mk - -# ---------------------------------------------------- -# Target Specs -# ---------------------------------------------------- - -MODULES= \ - ipc_tree \ - percept_SUITE \ - egd_SUITE - -EBIN = . - -HRL_FILES= - -ERL_FILES= $(MODULES:%=%.erl) - -TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) - -SOURCE = $(ERL_FILES) $(HRL_FILES) - -EMAKEFILE=Emakefile - -# ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/percept_test - -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -ERL_MAKE_FLAGS += -ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/percept/include - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- - -make_emakefile: - $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ - > $(EMAKEFILE) - -tests debug opt: make_emakefile - erl $(ERL_MAKE_FLAGS) -make - -clean: - rm -f $(EMAKEFILE) - rm -f $(TARGET_FILES) - rm -f core *~ - -docs: - - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_spec: opt - -release_tests_spec: make_emakefile - $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) percept.spec percept.cover $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)" - chmod -R u+w "$(RELSYSDIR)" - @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) - -release_docs_spec: - - diff --git a/lib/percept/test/egd_SUITE.erl b/lib/percept/test/egd_SUITE.erl deleted file mode 100644 index 401695dddd..0000000000 --- a/lib/percept/test/egd_SUITE.erl +++ /dev/null @@ -1,389 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - --module(egd_SUITE). --include_lib("common_test/include/ct.hrl"). - -%% Test server specific exports --export([all/0, suite/0]). --export([init_per_suite/1, end_per_suite/1]). --export([init_per_testcase/2, end_per_testcase/2]). - -%% Test cases --export([image_create_and_destroy/1, - image_shape/1, - image_primitives/1, - image_colors/1, - image_font/1, - image_fans/1, - image_png_compliant/1]). - -suite() -> - [{ct_hooks,[ts_install_cth]}, - {timetrap, {minutes, 1}}]. - -all() -> - [image_create_and_destroy, image_shape, - image_primitives, image_colors, image_font, - image_fans, - image_png_compliant]. - - -init_per_suite(Config) when is_list(Config) -> - rand:seed(exsplus), - Config. - -end_per_suite(Config) when is_list(Config) -> - Config. - -init_per_testcase(_Case, Config) -> - [{max_size, 800}|Config]. - -end_per_testcase(_Case, _Config) -> - ok. - -%%---------------------------------------------------------------------- -%% Tests -%%---------------------------------------------------------------------- - -%% Image creation and destroy test. -image_create_and_destroy(Config) when is_list(Config) -> - {W,H} = get_size(proplists:get_value(max_size, Config)), - Image = egd:create(W, H), - ok = egd:destroy(Image), - ok. - -%% Image color test. -image_colors(Config) when is_list(Config) -> - {W,H} = get_size(proplists:get_value(max_size, Config)), - Dir = proplists:get_value(priv_dir, Config), - Image = egd:create(W, H), - put(image_size, {W,H}), - - RGB = get_rgb(), - Black = egd:color({0,0,0}), - Red = egd:color({255,0,0}), - Green = egd:color({0,255,0}), - Blue = egd:color({0,0,255}), - Random = egd:color(Image, RGB), - - ok = egd:line(Image, get_point(), get_point(), Random), - ok = egd:line(Image, get_point(), get_point(), Red), - ok = egd:line(Image, get_point(), get_point(), Green), - ok = egd:line(Image, get_point(), get_point(), Black), - ok = egd:line(Image, get_point(), get_point(), Blue), - - HtmlDefaultNames = [black,silver,gray,white,maroon,red, - purple,fuchia,green,lime,olive,yellow,navy,blue,teal, - aqua], - - lists:foreach(fun (ColorName) -> - Color = egd:color(ColorName), - ok = egd:line(Image, get_point(), get_point(), Color) - end, HtmlDefaultNames), - - Png1 = <<_/binary>> = egd:render(Image,png,[{render_engine, alpha}]), - File1 = filename:join(Dir,"image_colors_alpha.png"), - ok = egd:save(Png1,File1), - ct:log("<p>Image alpha:</p><img src=\"~s\" />~n", [File1]), - Png2 = <<_/binary>> = egd:render(Image,png,[{render_engine, opaque}]), - File2 = filename:join(Dir,"image_colors_opaque.png"), - ok = egd:save(Png2,File2), - ct:log("<p>Image opaque:</p><img src=\"~s\" />~n", [File2]), - - ok = egd:destroy(Image), - erase(image_size), - ok. - -%% Image shape API test. -image_shape(Config) when is_list(Config) -> - {W,H} = get_size(proplists:get_value(max_size, Config)), - Dir = proplists:get_value(priv_dir, Config), - put(image_size, {W,H}), - Im = egd:create(W, H), - - Fgc = egd:color({255,0,0}), - - ok = egd:line(Im, get_point(), get_point(), Fgc), - ok = egd:rectangle(Im, get_point(), get_point(), Fgc), - ok = egd:filledEllipse(Im, get_point(), get_point(), Fgc), - ok = egd:arc(Im, get_point(), get_point(), Fgc), - ok = egd:arc(Im, get_point(), get_point(), 100, Fgc), - - Pt1 = get_point(), - Pt2 = get_point(), - - ok = egd:filledRectangle(Im, Pt1, Pt2, Fgc), - - Bitmap = egd:render(Im, raw_bitmap), - - ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc), - ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc), - - Bin = <<_/binary>> = egd:render(Im, raw_bitmap, [{render_engine, alpha}]), - Png = egd_png:binary(W,H,Bin), - File = filename:join(Dir,"image_shape.png"), - ok = egd:save(Png,File), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File]), - - ok = egd:destroy(Im), - - erase(image_size), - ok. - -%% Image shape API test. -image_primitives(Config) when is_list(Config) -> - {W,H} = get_size(proplists:get_value(max_size, Config)), - Dir = proplists:get_value(priv_dir, Config), - put(image_size, {W,H}), - - Im0 = egd_primitives:create(W, H), - Fgc = egd:color({25,25,255}), - Bgc = egd:color({0,250,25}), - - Im1 = lists:foldl(fun ({Function, Arguments}, Im) -> - erlang:apply(egd_primitives, Function, [Im|Arguments]) - end, Im0, - [{Fs, [get_point(), get_point(), Bgc]} || Fs <- [line, rectangle, filledEllipse, arc]] ++ - [{pixel, [get_point(), Bgc]}, - {filledTriangle, [get_point(), get_point(), get_point(), Bgc]}]), - - Pt1 = get_point(), - Pt2 = get_point(), - - Im2 = egd_primitives:filledRectangle(Im1, Pt1, Pt2, Fgc), - - Bitmap = egd_render:binary(Im2, opaque), - - ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc), - ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc), - - Bin = <<_/binary>> = egd_render:binary(Im2, alpha), - Png = egd_png:binary(W,H,Bin), - File = filename:join(Dir,"image_primitives.png"), - ok = egd:save(Png,File), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File]), - - erase(image_size), - ok. - -%% Image font test. -image_font(Config) when is_list(Config) -> - {W,H} = get_size(proplists:get_value(max_size, Config)), - Dir = proplists:get_value(priv_dir, Config), - put(image_size, {W,H}), - Im = egd:create(W, H), - Fgc = egd:color({0,130,0}), - - Filename = filename:join([code:priv_dir(percept),"fonts","6x11_latin1.wingsfont"]), - Font = egd_font:load(Filename), - - % simple text - ok = egd:text(Im, get_point(), Font, "Hello World", Fgc), - <<_/binary>> = egd:render(Im, png), - - GlyphStr1 = " !\"#$%&'()*+,-./", % Codes 32 -> 47 - NumericStr = "0123456789", % Codes 48 -> 57 - GlyphStr2 = ":;<=>?@", % Codes 58 -> 64 - AlphaBigStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", % Codes 65 -> 90 - GlyphStr3 = "[\\]^_`", % Codes 91 -> 96 - AlphaSmStr = "abcdefghijklmnopqrstuvwxyz", % Codes 97 -> 122 - GlyphStr4 = "{|}~", % Codes 123 -> 126 - - ok = egd:text(Im, get_point(), Font, GlyphStr1, Fgc), - Png1 = <<_/binary>> = egd:render(Im, png), - File1 = filename:join(Dir,"text1.png"), - ok = egd:save(Png1,File1), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File1]), - - ok = egd:text(Im, get_point(), Font, NumericStr, Fgc), - Png2 = <<_/binary>> = egd:render(Im, png), - File2 = filename:join(Dir,"text2.png"), - ok = egd:save(Png2,File2), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File2]), - - ok = egd:text(Im, get_point(), Font, GlyphStr2, Fgc), - Png3 = <<_/binary>> = egd:render(Im, png), - File3 = filename:join(Dir,"text3.png"), - ok = egd:save(Png3,File3), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File3]), - - ok = egd:text(Im, get_point(), Font, AlphaBigStr, Fgc), - Png4 = <<_/binary>> = egd:render(Im, png), - File4 = filename:join(Dir,"text4.png"), - ok = egd:save(Png4,File4), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File4]), - - ok = egd:text(Im, get_point(), Font, GlyphStr3, Fgc), - Png5 = <<_/binary>> = egd:render(Im, png), - File5 = filename:join(Dir,"text5.png"), - ok = egd:save(Png5,File5), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File5]), - - ok = egd:text(Im, get_point(), Font, AlphaSmStr, Fgc), - Png6 = <<_/binary>> = egd:render(Im, png), - File6 = filename:join(Dir,"text6.png"), - ok = egd:save(Png6,File6), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File6]), - - ok = egd:text(Im, get_point(), Font, GlyphStr4, Fgc), - Png7 = <<_/binary>> = egd:render(Im, png), - File7 = filename:join(Dir,"text7.png"), - ok = egd:save(Png7,File7), - ct:log("<p>Image:</p><img src=\"~s\" />~n", [File7]), - - ok = egd:destroy(Im), - erase(image_size), - ok. - -%% Image png compliant test. -image_png_compliant(Config) when is_list(Config) -> - {W,H} = get_size(proplists:get_value(max_size, Config)), - put(image_size, {W,H}), - Im = egd:create(W, H), - Fgc = egd:color({0,0,0}), - ok = egd:filledRectangle(Im, get_point(), get_point(), Fgc), - - Bin = egd:render(Im, png), - true = binary_is_png_compliant(Bin), - - ok = egd:destroy(Im), - erase(image_size), - ok. - -image_fans(Config) when is_list(Config) -> - W = 1024, - H = 800, - Dir = proplists:get_value(priv_dir, Config), - - Fun = fun({F,Args},Im) -> - erlang:apply(egd_primitives,F,[Im|Args]) - end, - - %% fan1 - Ops1 = gen_vertical_fan(1,{0,400},egd:color(red),1024,800,-15), - Ops2 = gen_horizontal_fan(1,{512,800},egd:color(green),1024,0,-15), - - Im0 = egd_primitives:create(W,H), - Im1 = lists:foldl(Fun, Im0, Ops1 ++ Ops2), - Bin1 = egd_render:binary(Im1, opaque), - Png1 = egd_png:binary(W,H,Bin1), - - File1 = filename:join(Dir,"fan1_opaque.png"), - ok = egd:save(Png1,File1), - ct:log("<p>Image opaque width 1:</p><img src=\"~s\" />~n", [File1]), - - Bin2 = egd_render:binary(Im1, alpha), - Png2 = egd_png:binary(W,H,Bin2), - - File2 = filename:join(Dir,"fan1_alpha.png"), - ok = egd:save(Png2,File2), - ct:log("<p>Image alpha width 1:</p><img src=\"~s\" />~n", [File2]), - - - %% fan2 - Ops3 = gen_vertical_fan(7,{0,400},egd:color(red),1024,800,-15), - Ops4 = gen_horizontal_fan(7,{512,800},egd:color(green),1024,0,-15), - - Im2 = lists:foldl(Fun, Im0, Ops3 ++ Ops4), - Bin3 = egd_render:binary(Im2, opaque), - Png3 = egd_png:binary(W,H,Bin3), - - File3 = filename:join(Dir,"fan2_opaque.png"), - ok = egd:save(Png3,File3), - ct:log("<p>Image opaque width 7:</p><img src=\"~s\" />~n", [File3]), - - Bin4 = egd_render:binary(Im2, alpha), - Png4 = egd_png:binary(W,H,Bin4), - - File4 = filename:join(Dir,"fan2_alpha.png"), - ok = egd:save(Png4,File4), - ct:log("<p>Image alpha width 7:</p><img src=\"~s\" />~n", [File4]), - ok. - -gen_vertical_fan(Wd,Pt,C,X,Y,Step) when Y > 0 -> - [{line,[Pt,{X,Y},Wd,C]}|gen_vertical_fan(Wd,Pt,C,X,Y + Step,Step)]; -gen_vertical_fan(_,_,_,_,_,_) -> []. - -gen_horizontal_fan(Wd,Pt,C,X,Y,Step) when X > 0 -> - [{line,[Pt,{X,Y},Wd,C]}|gen_horizontal_fan(Wd,Pt,C,X + Step,Y,Step)]; -gen_horizontal_fan(_,_,_,_,_,_) -> []. - - -%%---------------------------------------------------------------------- -%% Auxiliary tests -%%---------------------------------------------------------------------- - -bitmap_point_has_color(Bitmap, {W,_}, {X,Y}, C) -> - {CR,CG,CB,_} = egd_primitives:rgb_float2byte(C), - N = W*Y*3 + X*3, - << _:N/binary, R,G,B, _/binary>> = Bitmap, - case {R,G,B} of - {CR,CG,CB} -> ok; - Other -> - io:format("bitmap_point_has_color: error color was ~p, should be ~p~n", [Other, {CR,CG,CB}]), - {error, {Other,{CR,CG,CB}}} - end. - -binary_is_png_compliant(PngBin) -> - {Bin, _} = split_binary(PngBin, 10), - List = binary_to_list(Bin), - case lists:sublist(List, 2,3) of - "PNG" -> true; - Other -> - io:format("img -> ~p~n", [Other]), - false - end. - -%%---------------------------------------------------------------------- -%% Auxiliary -%%---------------------------------------------------------------------- - - -get_rgb() -> - R = random(255), - G = random(255), - B = random(255), - {R,G,B}. - -get_angle() -> - random(359). - -get_point() -> - get_point(get(image_size)). -get_point({W,H}) -> - X = random(W - 1), - Y = random(H - 1), - {X,Y}. - -get_size(Max) -> - W = trunc(random(Max/2) + Max/2 + 1), - H = trunc(random(Max/2) + Max/2 + 1), - io:format("Image size will be ~p x ~p~n", [W,H]), - {W,H}. - -get_points(N) -> - get_points(N, []). -get_points(0, Out) -> - Out; -get_points(N, Out) -> - get_points(N - 1, [get_point() | Out]). - -random(N) -> trunc(rand:uniform(trunc(N + 1)) - 1). diff --git a/lib/percept/test/ipc_tree.erl b/lib/percept/test/ipc_tree.erl deleted file mode 100644 index 29da20e83f..0000000000 --- a/lib/percept/test/ipc_tree.erl +++ /dev/null @@ -1,49 +0,0 @@ -%% ``Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. -%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings -%% AB. All Rights Reserved.'' -%% -%% $Id$ -%% - --module(ipc_tree). --export([go/1, init/2]). - -go(N) -> - start(N, self()), - receive stop -> ok end. - -start(Depth, ParentPid) -> - spawn(?MODULE, init, [Depth, ParentPid]). - -init(0, ParentPid) -> - workload(5000), - ParentPid ! stop, - ok; -init(Depth, ParentPid) -> - Pid1 = spawn(?MODULE, init, [Depth - 1, self()]), - Pid2 = spawn(?MODULE, init, [Depth - 1, self()]), - main([Pid1,Pid2], ParentPid). - -main(Pids, ParentPid) -> - workload(5000), - gather(Pids), - ParentPid ! stop, - ok. - -gather([]) -> ok; -gather([_|Pids]) -> receive _ -> gather(Pids) end. - -workload(0) -> ok; -workload(N) -> _ = math:sin(2), workload(N - 1). diff --git a/lib/percept/test/percept.cover b/lib/percept/test/percept.cover deleted file mode 100644 index 8a5ad0a55e..0000000000 --- a/lib/percept/test/percept.cover +++ /dev/null @@ -1,2 +0,0 @@ -{incl_app,percept,details}. - diff --git a/lib/percept/test/percept.spec b/lib/percept/test/percept.spec deleted file mode 100644 index f3ef76bd60..0000000000 --- a/lib/percept/test/percept.spec +++ /dev/null @@ -1 +0,0 @@ -{suites,"../percept_test",all}. diff --git a/lib/percept/test/percept_SUITE.erl b/lib/percept/test/percept_SUITE.erl deleted file mode 100644 index 2be8b70e0d..0000000000 --- a/lib/percept/test/percept_SUITE.erl +++ /dev/null @@ -1,126 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - --module(percept_SUITE). --include_lib("common_test/include/ct.hrl"). - -%% Test server specific exports --export([all/0, suite/0]). - -%% Test cases --export([app/1, - appup/1, - profile/1, - analyze/1, - analyze_dist/1, - webserver/1]). - -suite() -> - [{ct_hooks,[ts_install_cth]}, - {timetrap, {minutes, 2}}]. - -all() -> - [app, appup, webserver, profile, - analyze, analyze_dist]. - - -%%---------------------------------------------------------------------- -%% Tests -%%---------------------------------------------------------------------- - -%% Test that the percept app file is ok -app(Config) when is_list(Config) -> - ok = test_server:app_test(percept). - -%% Test that the percept appup file is ok -appup(Config) when is_list(Config) -> - ok = test_server:appup_test(percept). - -%% Percept webserver test. -webserver(Config) when is_list(Config) -> - % Explicit start inets? - {started, _, Port} = percept:start_webserver(), - ok = percept:stop_webserver(Port), - {started, _, _} = percept:start_webserver(), - ok = percept:stop_webserver(), - {started, _, NewPort} = percept:start_webserver(), - ok = percept:stop_webserver(NewPort), - application:stop(inets), - ok. - -%% Percept profile test. -profile(Config) when is_list(Config) -> - Path = proplists:get_value(data_dir, Config), - File = filename:join([Path,"profile_test.dat"]), - {ok, _} = percept:profile(File, [procs]), - ipc_tree:go(7), - ok = percept:stop_profile(), - ok. - -%% Percept analyze test. -analyze(Config) when is_list(Config) -> - Begin = processes(), - Path = proplists:get_value(data_dir, Config), - File = filename:join([Path,"profile_test.dat"]), - T0 = erlang:monotonic_time(millisecond), - ok = percept:analyze(File), - T1 = erlang:monotonic_time(millisecond), - io:format("percept:analyze/1 took ~w ms.~n", [T1 - T0]), - {stopped, _} = percept_db:stop(), - print_remainers(remainers(Begin, processes())), - ok. - -%% Percept analyze distribution test. -analyze_dist(Config) when is_list(Config) -> - Begin = processes(), - Path = proplists:get_value(data_dir, Config), - File = filename:join([Path,"ipc-dist.dat"]), - T0 = erlang:monotonic_time(millisecond), - ok = percept:analyze(File), - T1 = erlang:monotonic_time(millisecond), - io:format("percept:analyze/1 took ~w ms.~n", [T1 - T0]), - {stopped, _} = percept_db:stop(), - print_remainers(remainers(Begin, processes())), - ok. - -%%---------------------------------------------------------------------- -%% Auxiliary tests -%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Auxiliary -%%---------------------------------------------------------------------- - -print_remainers([]) -> ok; -print_remainers([Pid|Pids]) -> - io:format("[Pid ~p] [Entry ~p] [Name ~p]~n", [ - Pid, - erlang:process_info(Pid, initial_call), - erlang:process_info(Pid, registered_name) - ]), - print_remainers(Pids). - -remainers(Begin, End) -> remainers(Begin, End, []). -remainers(_, [], Out) -> lists:reverse(Out); -remainers(Begin, [Pid|End], Out) -> - case lists:member(Pid, Begin) of - true -> remainers(Begin, End, Out); - false -> remainers(Begin, End, [Pid|Out]) - end. diff --git a/lib/percept/test/percept_SUITE_data/ipc-dist.dat b/lib/percept/test/percept_SUITE_data/ipc-dist.dat Binary files differdeleted file mode 100644 index 14ab6c0c5d..0000000000 --- a/lib/percept/test/percept_SUITE_data/ipc-dist.dat +++ /dev/null diff --git a/lib/percept/test/percept_db_SUITE.erl b/lib/percept/test/percept_db_SUITE.erl deleted file mode 100644 index b2827e0e42..0000000000 --- a/lib/percept/test/percept_db_SUITE.erl +++ /dev/null @@ -1,55 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - --module(percept_db_SUITE). --include_lib("common_test/include/ct.hrl"). - -%% Test server specific exports --export([all/0, suite/0]). - -%% Test cases --export([start/1]). - -%% Default timetrap timeout (set in init_per_testcase) --define(restarts, 10). --define(alive_timeout, 500). - -suite() -> - [{timetrap, {minutes, 2}}]. - -all() -> - [start]. - -%%---------------------------------------------------------------------- -%% Tests -%%---------------------------------------------------------------------- - -%% Percept_db start and restart test. -start(Config) when is_list(Config) -> - ok = restart(?restarts), - {stopped, _DB} = percept_db:stop(), - ok. - -restart(0)-> ok; -restart(N)-> - {_, DB} = percept_db:start(), - timer:sleep(?alive_timeout), - true = erlang:is_process_alive(DB), - restart(N-1). diff --git a/lib/percept/vsn.mk b/lib/percept/vsn.mk deleted file mode 100644 index 614cee8645..0000000000 --- a/lib/percept/vsn.mk +++ /dev/null @@ -1 +0,0 @@ -PERCEPT_VSN = 0.9 diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index 2c902952a1..0ef6b1c521 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -42,7 +42,6 @@ MODULES= \ runtime_tools_sup \ dbg \ dyntrace \ - percept_profile \ system_information \ observer_backend \ ttb_autostart\ diff --git a/lib/runtime_tools/src/percept_profile.erl b/lib/runtime_tools/src/percept_profile.erl deleted file mode 100644 index 1e8e913b80..0000000000 --- a/lib/runtime_tools/src/percept_profile.erl +++ /dev/null @@ -1,195 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%% @doc Percept Collector -%% -%% This module provides the user interface for the percept data -% collection (profiling). -%% - --module(percept_profile). --export([ - start/1, - start/2, - start/3, - stop/0 - ]). - - -%%========================================================================== -%% -%% Type definitions -%% -%%========================================================================== - -%% @type percept_option() = procs | ports | exclusive - --type percept_option() :: 'procs' | 'ports' | 'exclusive' | 'scheduler'. - -%%========================================================================== -%% -%% Interface functions -%% -%%========================================================================== - -%% @spec start(Filename::string()) -> {ok, Port} | {already_started, Port} -%% @equiv start(Filename, [procs]) - --spec start(Filename :: file:filename()) -> - {'ok', port()} | {'already_started', port()}. - -start(Filename) -> - profile_to_file(Filename, [procs]). - -%% @spec start(Filename::string(), [percept_option()]) -> {ok, Port} | {already_started, Port} -%% Port = port() -%% @doc Starts profiling with supplied options. -%% All events are stored in the file given by Filename. -%% An explicit call to stop/0 is needed to stop profiling. - --spec start(Filename :: file:filename(), - Options :: [percept_option()]) -> - {'ok', port()} | {'already_started', port()}. - -start(Filename, Options) -> - profile_to_file(Filename, Options). - -%% @spec start(string(), MFA::mfa(), [percept_option()]) -> ok | {already_started, Port} | {error, not_started} -%% Port = port() -%% @doc Starts profiling at the entrypoint specified by the MFA. All events are collected, -%% this means that processes outside the scope of the entry-point are also profiled. -%% No explicit call to stop/0 is needed, the profiling stops when -%% the entry function returns. - --spec start(Filename :: file:filename(), - Entry :: {atom(), atom(), list()}, - Options :: [percept_option()]) -> - 'ok' | {'already_started', port()} | {'error', 'not_started'}. - -start(Filename, {Module, Function, Args}, Options) -> - case whereis(percept_port) of - undefined -> - {ok, _} = profile_to_file(Filename, Options), - erlang:apply(Module, Function, Args), - stop(); - Port -> - {already_started, Port} - end. - -deliver_all_trace() -> - Tracee = self(), - Tracer = spawn(fun() -> - receive {Tracee, start} -> ok end, - Ref = erlang:trace_delivered(Tracee), - receive {trace_delivered, Tracee, Ref} -> Tracee ! {self(), ok} end - end), - erlang:trace(Tracee, true, [procs, {tracer, Tracer}]), - Tracer ! {Tracee, start}, - receive {Tracer, ok} -> ok end, - erlang:trace(Tracee, false, [procs]), - ok. - -%% @spec stop() -> ok | {'error', 'not_started'} -%% @doc Stops profiling. - --spec stop() -> 'ok' | {'error', 'not_started'}. - -stop() -> - _ = erlang:system_profile(undefined, [runnable_ports, runnable_procs]), - erlang:trace(all, false, [procs, ports, timestamp]), - deliver_all_trace(), - case whereis(percept_port) of - undefined -> - {error, not_started}; - Port -> - erlang:port_command(Port, erlang:term_to_binary({profile_stop, erlang:timestamp()})), - %% trace delivered? - erlang:port_close(Port), - ok - end. - -%%========================================================================== -%% -%% Auxiliary functions -%% -%%========================================================================== - -profile_to_file(Filename, Opts) -> - case whereis(percept_port) of - undefined -> - io:format("Starting profiling.~n", []), - - erlang:system_flag(multi_scheduling, block), - Port = (dbg:trace_port(file, Filename))(), - % Send start time - erlang:port_command(Port, erlang:term_to_binary({profile_start, erlang:timestamp()})), - erlang:system_flag(multi_scheduling, unblock), - - %% Register Port - erlang:register(percept_port, Port), - set_tracer(Port, Opts), - {ok, Port}; - Port -> - io:format("Profiling already started at port ~p.~n", [Port]), - {already_started, Port} - end. - -%% set_tracer - -set_tracer(Port, Opts) -> - {TOpts, POpts} = parse_profile_options(Opts), - % Setup profiling and tracing - erlang:trace(all, true, [{tracer, Port}, timestamp | TOpts]), - _ = erlang:system_profile(Port, POpts), - ok. - -%% parse_profile_options - -parse_profile_options(Opts) -> - parse_profile_options(Opts, {[],[]}). - -parse_profile_options([], Out) -> - Out; -parse_profile_options([Opt|Opts], {TOpts, POpts}) -> - case Opt of - procs -> - parse_profile_options(Opts, { - [procs | TOpts], - [runnable_procs | POpts] - }); - ports -> - parse_profile_options(Opts, { - [ports | TOpts], - [runnable_ports | POpts] - }); - scheduler -> - parse_profile_options(Opts, { - TOpts, - [scheduler | POpts] - }); - exclusive -> - parse_profile_options(Opts, { - TOpts, - [exclusive | POpts] - }); - _ -> - parse_profile_options(Opts, {TOpts, POpts}) - end. diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index 690c61a4c3..d6c1f17e70 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -20,8 +20,8 @@ {application, runtime_tools, [{description, "RUNTIME_TOOLS"}, {vsn, "%VSN%"}, - {modules, [appmon_info, dbg,observer_backend,percept_profile, - runtime_tools,runtime_tools_sup,erts_alloc_config, + {modules, [appmon_info, dbg,observer_backend,runtime_tools, + runtime_tools_sup,erts_alloc_config, ttb_autostart,dyntrace,system_information, msacc]}, {registered, [runtime_tools_sup]}, @@ -30,5 +30,3 @@ {mod, {runtime_tools, []}}, {runtime_dependencies, ["stdlib-3.0","mnesia-4.12","kernel-5.0", "erts-8.0"]}]}. - - diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index 3dda1a3316..2e7df9792e 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -48,9 +48,17 @@ MODULES= \ dtls \ ssl_alert \ ssl_app \ - ssl_dist_sup\ ssl_sup \ + ssl_admin_sup\ + tls_connection_sup \ + ssl_connection_sup \ + ssl_listen_tracker_sup\ + dtls_connection_sup \ + dtls_udp_listener\ dtls_udp_sup \ + ssl_dist_sup\ + ssl_dist_admin_sup\ + ssl_dist_connection_sup\ inet_tls_dist \ inet6_tls_dist \ ssl_certificate\ @@ -61,21 +69,18 @@ MODULES= \ dtls_connection \ ssl_config \ ssl_connection \ - tls_connection_sup \ - dtls_connection_sup \ tls_handshake \ dtls_handshake\ ssl_handshake\ ssl_manager \ ssl_session \ ssl_session_cache \ + ssl_pem_cache \ ssl_crl\ ssl_crl_cache \ ssl_crl_hash_dir \ tls_socket \ dtls_socket \ - dtls_udp_listener\ - ssl_listen_tracker_sup \ tls_record \ dtls_record \ ssl_record \ diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 9c5d795848..148989174d 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -10,12 +10,14 @@ tls_v1, ssl_v3, ssl_v2, + tls_connection_sup, %% DTLS dtls_connection, dtls_handshake, dtls_record, dtls_socket, dtls_v1, + dtls_connection_sup, dtls_udp_listener, dtls_udp_sup, %% API @@ -31,16 +33,19 @@ ssl_cipher, ssl_srp_primes, ssl_alert, - ssl_listen_tracker_sup, + ssl_listen_tracker_sup, %% may be used by DTLS over SCTP %% Erlang Distribution over SSL/TLS inet_tls_dist, inet6_tls_dist, ssl_tls_dist_proxy, ssl_dist_sup, - %% SSL/TLS session handling + ssl_dist_connection_sup, + ssl_dist_admin_sup, + %% SSL/TLS session and cert handling ssl_session, ssl_session_cache, ssl_manager, + ssl_pem_cache, ssl_pkix_db, ssl_certificate, %% CRL handling @@ -51,8 +56,8 @@ %% App structure ssl_app, ssl_sup, - tls_connection_sup, - dtls_connection_sup + ssl_admin_sup, + ssl_connection_sup ]}, {registered, [ssl_sup, ssl_manager]}, {applications, [crypto, public_key, kernel, stdlib]}, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 0b7229b67e..4a5a7e25ea 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -577,7 +577,7 @@ prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) -> %% Description: Clear the PEM cache %%-------------------------------------------------------------------- clear_pem_cache() -> - ssl_manager:clear_pem_cache(). + ssl_pem_cache:clear(). %%--------------------------------------------------------------- -spec format_error({error, term()}) -> list(). diff --git a/lib/ssl/src/ssl_admin_sup.erl b/lib/ssl/src/ssl_admin_sup.erl new file mode 100644 index 0000000000..9c96435753 --- /dev/null +++ b/lib/ssl/src/ssl_admin_sup.erl @@ -0,0 +1,95 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssl_admin_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0, manager_opts/0]). + +%% Supervisor callback +-export([init/1]). + +%%%========================================================================= +%%% API +%%%========================================================================= + +-spec start_link() -> {ok, pid()} | ignore | {error, term()}. + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= + +init([]) -> + PEMCache = pem_cache_child_spec(), + SessionCertManager = session_and_cert_manager_child_spec(), + {ok, {{rest_for_one, 10, 3600}, [PEMCache, SessionCertManager]}}. + +manager_opts() -> + CbOpts = case application:get_env(ssl, session_cb) of + {ok, Cb} when is_atom(Cb) -> + InitArgs = session_cb_init_args(), + [{session_cb, Cb}, {session_cb_init_args, InitArgs}]; + _ -> + [] + end, + case application:get_env(ssl, session_lifetime) of + {ok, Time} when is_integer(Time) -> + [{session_lifetime, Time}| CbOpts]; + _ -> + CbOpts + end. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +pem_cache_child_spec() -> + Name = ssl_pem_cache, + StartFunc = {ssl_pem_cache, start_link, [[]]}, + Restart = permanent, + Shutdown = 4000, + Modules = [ssl_pem_cache], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +session_and_cert_manager_child_spec() -> + Opts = manager_opts(), + Name = ssl_manager, + StartFunc = {ssl_manager, start_link, [Opts]}, + Restart = permanent, + Shutdown = 4000, + Modules = [ssl_manager], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +session_cb_init_args() -> + case application:get_env(ssl, session_cb_init_args) of + {ok, Args} when is_list(Args) -> + Args; + _ -> + [] + end. diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl index 0652d029c3..54f83928ee 100644 --- a/lib/ssl/src/ssl_config.erl +++ b/lib/ssl/src/ssl_config.erl @@ -41,9 +41,11 @@ init(SslOpts, Role) -> {ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, PrivateKey, DHParams}. init_manager_name(false) -> - put(ssl_manager, ssl_manager:manager_name(normal)); + put(ssl_manager, ssl_manager:name(normal)), + put(ssl_cache, ssl_pem_cache:name(normal)); init_manager_name(true) -> - put(ssl_manager, ssl_manager:manager_name(dist)). + put(ssl_manager, ssl_manager:name(dist)), + put(ssl_cache, ssl_pem_cache:name(dist)). init_certificates(#ssl_options{cacerts = CaCerts, cacertfile = CACertFile, @@ -135,6 +137,8 @@ file_error(File, Throw) -> case Throw of {Opt,{badmatch, {error, {badmatch, Error}}}} -> throw({options, {Opt, binary_to_list(File), Error}}); + {Opt, {badmatch, Error}} -> + throw({options, {Opt, binary_to_list(File), Error}}); _ -> throw(Throw) end. diff --git a/lib/ssl/src/ssl_connection_sup.erl b/lib/ssl/src/ssl_connection_sup.erl new file mode 100644 index 0000000000..1a1f43e683 --- /dev/null +++ b/lib/ssl/src/ssl_connection_sup.erl @@ -0,0 +1,101 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssl_connection_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callback +-export([init/1]). + +%%%========================================================================= +%%% API +%%%========================================================================= + +-spec start_link() -> {ok, pid()} | ignore | {error, term()}. + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= + +init([]) -> + + TLSConnetionManager = tls_connection_manager_child_spec(), + %% Handles emulated options so that they inherited by the accept + %% socket, even when setopts is performed on the listen socket + ListenOptionsTracker = listen_options_tracker_child_spec(), + + DTLSConnetionManager = dtls_connection_manager_child_spec(), + DTLSUdpListeners = dtls_udp_listeners_spec(), + + {ok, {{one_for_one, 10, 3600}, [TLSConnetionManager, + ListenOptionsTracker, + DTLSConnetionManager, + DTLSUdpListeners + ]}}. + + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +tls_connection_manager_child_spec() -> + Name = tls_connection, + StartFunc = {tls_connection_sup, start_link, []}, + Restart = permanent, + Shutdown = 4000, + Modules = [tls_connection_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +dtls_connection_manager_child_spec() -> + Name = dtls_connection, + StartFunc = {dtls_connection_sup, start_link, []}, + Restart = permanent, + Shutdown = 4000, + Modules = [dtls_connection_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +listen_options_tracker_child_spec() -> + Name = tls_socket, + StartFunc = {ssl_listen_tracker_sup, start_link, []}, + Restart = permanent, + Shutdown = 4000, + Modules = [tls_socket], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +dtls_udp_listeners_spec() -> + Name = dtls_udp_listener, + StartFunc = {dtls_udp_sup, start_link, []}, + Restart = permanent, + Shutdown = 4000, + Modules = [], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. diff --git a/lib/ssl/src/ssl_dist_admin_sup.erl b/lib/ssl/src/ssl_dist_admin_sup.erl new file mode 100644 index 0000000000..f60806c4cb --- /dev/null +++ b/lib/ssl/src/ssl_dist_admin_sup.erl @@ -0,0 +1,74 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssl_dist_admin_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callback +-export([init/1]). + +%%%========================================================================= +%%% API +%%%========================================================================= + +-spec start_link() -> {ok, pid()} | ignore | {error, term()}. + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= + +init([]) -> + PEMCache = pem_cache_child_spec(), + SessionCertManager = session_and_cert_manager_child_spec(), + {ok, {{rest_for_one, 10, 3600}, [PEMCache, SessionCertManager]}}. + + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +pem_cache_child_spec() -> + Name = ssl_pem_cache_dist, + StartFunc = {ssl_pem_cache, start_link_dist, [[]]}, + Restart = permanent, + Shutdown = 4000, + Modules = [ssl_pem_cache], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +session_and_cert_manager_child_spec() -> + Opts = ssl_admin_sup:manager_opts(), + Name = ssl_dist_manager, + StartFunc = {ssl_manager, start_link_dist, [Opts]}, + Restart = permanent, + Shutdown = 4000, + Modules = [ssl_manager], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + diff --git a/lib/ssl/src/ssl_dist_connection_sup.erl b/lib/ssl/src/ssl_dist_connection_sup.erl new file mode 100644 index 0000000000..e5842c866e --- /dev/null +++ b/lib/ssl/src/ssl_dist_connection_sup.erl @@ -0,0 +1,79 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssl_dist_connection_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callback +-export([init/1]). + +%%%========================================================================= +%%% API +%%%========================================================================= + +-spec start_link() -> {ok, pid()} | ignore | {error, term()}. + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= + +init([]) -> + + TLSConnetionManager = tls_connection_manager_child_spec(), + %% Handles emulated options so that they inherited by the accept + %% socket, even when setopts is performed on the listen socket + ListenOptionsTracker = listen_options_tracker_child_spec(), + + {ok, {{one_for_one, 10, 3600}, [TLSConnetionManager, + ListenOptionsTracker + ]}}. + + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +tls_connection_manager_child_spec() -> + Name = dist_tls_connection, + StartFunc = {tls_connection_sup, start_link_dist, []}, + Restart = permanent, + Shutdown = 4000, + Modules = [tls_connection_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +listen_options_tracker_child_spec() -> + Name = dist_tls_socket, + StartFunc = {ssl_listen_tracker_sup, start_link_dist, []}, + Restart = permanent, + Shutdown = 4000, + Modules = [tls_socket], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl index d47cd76bf5..690b896919 100644 --- a/lib/ssl/src/ssl_dist_sup.erl +++ b/lib/ssl/src/ssl_dist_sup.erl @@ -44,34 +44,29 @@ start_link() -> %%%========================================================================= init([]) -> - SessionCertManager = session_and_cert_manager_child_spec(), - ConnetionManager = connection_manager_child_spec(), - ListenOptionsTracker = listen_options_tracker_child_spec(), + AdminSup = ssl_admin_child_spec(), + ConnectionSup = ssl_connection_sup(), ProxyServer = proxy_server_child_spec(), - - {ok, {{one_for_all, 10, 3600}, [SessionCertManager, ConnetionManager, - ListenOptionsTracker, - ProxyServer]}}. + {ok, {{one_for_all, 10, 3600}, [AdminSup, ProxyServer, ConnectionSup]}}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -session_and_cert_manager_child_spec() -> - Opts = ssl_sup:manager_opts(), - Name = ssl_manager_dist, - StartFunc = {ssl_manager, start_link_dist, [Opts]}, +ssl_admin_child_spec() -> + Name = ssl_dist_admin_sup, + StartFunc = {ssl_dist_admin_sup, start_link , []}, Restart = permanent, Shutdown = 4000, - Modules = [ssl_manager], - Type = worker, + Modules = [ssl_admin_sup], + Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. -connection_manager_child_spec() -> - Name = ssl_connection_dist, - StartFunc = {tls_connection_sup, start_link_dist, []}, - Restart = permanent, - Shutdown = infinity, - Modules = [tls_connection_sup], +ssl_connection_sup() -> + Name = ssl_dist_connection_sup, + StartFunc = {ssl_dist_connection_sup, start_link, []}, + Restart = permanent, + Shutdown = 4000, + Modules = [ssl_connection_sup], Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. @@ -83,12 +78,3 @@ proxy_server_child_spec() -> Modules = [ssl_tls_dist_proxy], Type = worker, {Name, StartFunc, Restart, Shutdown, Type, Modules}. - -listen_options_tracker_child_spec() -> - Name = tls_socket_dist, - StartFunc = {ssl_listen_tracker_sup, start_link_dist, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_socket], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 5bd9521de7..29b15f843f 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -32,10 +32,9 @@ new_session_id/1, clean_cert_db/2, register_session/2, register_session/3, invalidate_session/2, insert_crls/2, insert_crls/3, delete_crls/1, delete_crls/2, - invalidate_session/3, invalidate_pem/1, clear_pem_cache/0, manager_name/1]). + invalidate_session/3, name/1]). -% Spawn export --export([init_session_validator/1, init_pem_cache_validator/1]). +-export([init_session_validator/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -52,9 +51,7 @@ session_lifetime :: integer(), certificate_db :: db_handle(), session_validation_timer :: reference(), - last_delay_timer = {undefined, undefined},%% Keep for testing purposes - last_pem_check :: erlang:timestamp(), - clear_pem_cache :: integer(), + last_delay_timer = {undefined, undefined},%% Keep for testing purposes session_cache_client_max :: integer(), session_cache_server_max :: integer(), session_server_invalidator :: undefined | pid(), @@ -63,7 +60,6 @@ -define(GEN_UNIQUE_ID_MAX_TRIES, 10). -define(SESSION_VALIDATION_INTERVAL, 60000). --define(CLEAR_PEM_CACHE, 120000). -define(CLEAN_SESSION_DB, 60000). -define(CLEAN_CERT_DB, 500). -define(DEFAULT_MAX_SESSION_CACHE, 1000). @@ -74,14 +70,14 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec manager_name(normal | dist) -> atom(). +-spec name(normal | dist) -> atom(). %% %% Description: Returns the registered name of the ssl manager process %% in the operation modes 'normal' and 'dist'. %%-------------------------------------------------------------------- -manager_name(normal) -> +name(normal) -> ?MODULE; -manager_name(dist) -> +name(dist) -> list_to_atom(atom_to_list(?MODULE) ++ "dist"). %%-------------------------------------------------------------------- @@ -91,9 +87,10 @@ manager_name(dist) -> %% and certificate caching. %%-------------------------------------------------------------------- start_link(Opts) -> - DistMangerName = manager_name(normal), - gen_server:start_link({local, DistMangerName}, - ?MODULE, [DistMangerName, Opts], []). + MangerName = name(normal), + CacheName = ssl_pem_cache:name(normal), + gen_server:start_link({local, MangerName}, + ?MODULE, [MangerName, CacheName, Opts], []). %%-------------------------------------------------------------------- -spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}. @@ -102,9 +99,10 @@ start_link(Opts) -> %% be used by the erlang distribution. Note disables soft upgrade! %%-------------------------------------------------------------------- start_link_dist(Opts) -> - DistMangerName = manager_name(dist), + DistMangerName = name(dist), + DistCacheName = ssl_pem_cache:name(dist), gen_server:start_link({local, DistMangerName}, - ?MODULE, [DistMangerName, Opts], []). + ?MODULE, [DistMangerName, DistCacheName, Opts], []). %%-------------------------------------------------------------------- -spec connection_init(binary()| {der, list()}, client | server, @@ -115,25 +113,10 @@ start_link_dist(Opts) -> %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init({der, _} = Trustedcerts, Role, CRLCache) -> - case bypass_pem_cache() of - true -> - {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts), - call({connection_init, Extracted, Role, CRLCache}); - false -> - call({connection_init, Trustedcerts, Role, CRLCache}) - end; - -connection_init(<<>> = Trustedcerts, Role, CRLCache) -> - call({connection_init, Trustedcerts, Role, CRLCache}); - + {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts), + call({connection_init, Extracted, Role, CRLCache}); connection_init(Trustedcerts, Role, CRLCache) -> - case bypass_pem_cache() of - true -> - {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts), - call({connection_init, Extracted, Role, CRLCache}); - false -> - call({connection_init, Trustedcerts, Role, CRLCache}) - end. + call({connection_init, Trustedcerts, Role, CRLCache}). %%-------------------------------------------------------------------- -spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}. @@ -141,31 +124,14 @@ connection_init(Trustedcerts, Role, CRLCache) -> %% Description: Cache a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - case bypass_pem_cache() of - true -> - ssl_pkix_db:decode_pem_file(File); - false -> - case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of - [{Content,_}] -> - {ok, Content}; - [Content] -> - {ok, Content}; - undefined -> - call({cache_pem, File}) - end + case ssl_pkix_db:lookup(File, DbHandle) of + [Content] -> + {ok, Content}; + undefined -> + ssl_pem_cache:insert(File) end. %%-------------------------------------------------------------------- --spec clear_pem_cache() -> ok. -%% -%% Description: Clear the PEM cache -%%-------------------------------------------------------------------- -clear_pem_cache() -> - %% Not supported for distribution at the moement, should it be? - put(ssl_manager, manager_name(normal)), - call(unconditionally_clear_pem_cache). - -%%-------------------------------------------------------------------- -spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) -> undefined | {ok, {der_cert(), #'OTPCertificate'{}}}. @@ -222,26 +188,22 @@ invalidate_session(Port, Session) -> load_mitigation(), cast({invalidate_session, Port, Session}). --spec invalidate_pem(File::binary()) -> ok. -invalidate_pem(File) -> - cast({invalidate_pem, File}). - insert_crls(Path, CRLs)-> insert_crls(Path, CRLs, normal). insert_crls(?NO_DIST_POINT_PATH = Path, CRLs, ManagerType)-> - put(ssl_manager, manager_name(ManagerType)), + put(ssl_manager, name(ManagerType)), cast({insert_crls, Path, CRLs}); insert_crls(Path, CRLs, ManagerType)-> - put(ssl_manager, manager_name(ManagerType)), + put(ssl_manager, name(ManagerType)), call({insert_crls, Path, CRLs}). delete_crls(Path)-> delete_crls(Path, normal). delete_crls(?NO_DIST_POINT_PATH = Path, ManagerType)-> - put(ssl_manager, manager_name(ManagerType)), + put(ssl_manager, name(ManagerType)), cast({delete_crls, Path}); delete_crls(Path, ManagerType)-> - put(ssl_manager, manager_name(ManagerType)), + put(ssl_manager, name(ManagerType)), call({delete_crls, Path}). %%==================================================================== @@ -255,8 +217,9 @@ delete_crls(Path, ManagerType)-> %% %% Description: Initiates the server %%-------------------------------------------------------------------- -init([Name, Opts]) -> - put(ssl_manager, Name), +init([ManagerName, PemCacheName, Opts]) -> + put(ssl_manager, ManagerName), + put(ssl_pem_cache, PemCacheName), process_flag(trap_exit, true), CacheCb = proplists:get_value(session_cb, Opts, ssl_session_cache), SessionLifeTime = @@ -270,16 +233,12 @@ init([Name, Opts]) -> proplists:get_value(session_cb_init_args, Opts, [])]), Timer = erlang:send_after(SessionLifeTime * 1000 + 5000, self(), validate_sessions), - Interval = pem_check_interval(), - erlang:send_after(Interval, self(), clear_pem_cache), {ok, #state{certificate_db = CertDb, session_cache_client = ClientSessionCache, session_cache_server = ServerSessionCache, session_cache_cb = CacheCb, session_lifetime = SessionLifeTime, session_validation_timer = Timer, - last_pem_check = os:timestamp(), - clear_pem_cache = Interval, session_cache_client_max = max_session_cache_size(session_cache_client_max), session_cache_server_max = @@ -330,21 +289,7 @@ handle_call({{new_session_id, Port}, _}, _, #state{session_cache_cb = CacheCb, session_cache_server = Cache} = State) -> Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb), - {reply, Id, State}; - -handle_call({{cache_pem,File}, _Pid}, _, - #state{certificate_db = Db} = State) -> - try ssl_pkix_db:cache_pem_file(File, Db) of - Result -> - {reply, Result, State} - catch - _:Reason -> - {reply, {error, Reason}, State} - end; -handle_call({unconditionally_clear_pem_cache, _},_, - #state{certificate_db = [_,_,PemChace | _]} = State) -> - ssl_pkix_db:clear(PemChace), - {reply, ok, State}. + {reply, Id, State}. %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. @@ -382,11 +327,6 @@ handle_cast({insert_crls, Path, CRLs}, handle_cast({delete_crls, CRLsOrPath}, #state{certificate_db = Db} = State) -> ssl_pkix_db:remove_crls(Db, CRLsOrPath), - {noreply, State}; - -handle_cast({invalidate_pem, File}, - #state{certificate_db = [_, _, PemCache | _]} = State) -> - ssl_pkix_db:remove(File, PemCache), {noreply, State}. %%-------------------------------------------------------------------- @@ -418,22 +358,14 @@ handle_info({delayed_clean_session, Key, Cache}, #state{session_cache_cb = Cache CacheCb:delete(Cache, Key), {noreply, State}; -handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace | _], - clear_pem_cache = Interval, - last_pem_check = CheckPoint} = State) -> - NewCheckPoint = os:timestamp(), - start_pem_cache_validator(PemChace, CheckPoint), - erlang:send_after(Interval, self(), clear_pem_cache), - {noreply, State#state{last_pem_check = NewCheckPoint}}; - handle_info({clean_cert_db, Ref, File}, - #state{certificate_db = [CertDb,RefDb, PemCache | _]} = State) -> + #state{certificate_db = [CertDb, {RefDb, FileMapDb} | _]} = State) -> case ssl_pkix_db:lookup(Ref, RefDb) of undefined -> %% Alredy cleaned ok; _ -> - clean_cert_db(Ref, CertDb, RefDb, PemCache, File) + clean_cert_db(Ref, CertDb, RefDb, FileMapDb, File) end, {noreply, State}; @@ -523,14 +455,6 @@ delay_time() -> ?CLEAN_SESSION_DB end. -bypass_pem_cache() -> - case application:get_env(ssl, bypass_pem_cache) of - {ok, Bool} when is_boolean(Bool) -> - Bool; - _ -> - false - end. - max_session_cache_size(CacheType) -> case application:get_env(ssl, CacheType) of {ok, Size} when is_integer(Size) -> @@ -594,16 +518,11 @@ new_id(Port, Tries, Cache, CacheCb) -> new_id(Port, Tries - 1, Cache, CacheCb) end. -clean_cert_db(Ref, CertDb, RefDb, PemCache, File) -> +clean_cert_db(Ref, CertDb, RefDb, FileMapDb, File) -> case ssl_pkix_db:ref_count(Ref, RefDb, 0) of 0 -> - case ssl_pkix_db:lookup_cached_pem(PemCache, File) of - [{Content, Ref}] -> - ssl_pkix_db:insert(File, Content, PemCache); - _ -> - ok - end, ssl_pkix_db:remove(Ref, RefDb), + ssl_pkix_db:remove(File, FileMapDb), ssl_pkix_db:remove_trusted_certs(Ref, CertDb); _ -> ok @@ -687,42 +606,6 @@ exists_equivalent(#session{ exists_equivalent(Session, [ _ | Rest]) -> exists_equivalent(Session, Rest). -start_pem_cache_validator(PemCache, CheckPoint) -> - spawn_link(?MODULE, init_pem_cache_validator, - [[get(ssl_manager), PemCache, CheckPoint]]). - -init_pem_cache_validator([SslManagerName, PemCache, CheckPoint]) -> - put(ssl_manager, SslManagerName), - ssl_pkix_db:foldl(fun pem_cache_validate/2, - CheckPoint, PemCache). - -pem_cache_validate({File, _}, CheckPoint) -> - case file:read_file_info(File, []) of - {ok, #file_info{mtime = Time}} -> - case is_before_checkpoint(Time, CheckPoint) of - true -> - ok; - false -> - invalidate_pem(File) - end; - _ -> - invalidate_pem(File) - end, - CheckPoint. - -pem_check_interval() -> - case application:get_env(ssl, ssl_pem_cache_clean) of - {ok, Interval} when is_integer(Interval) -> - Interval; - _ -> - ?CLEAR_PEM_CACHE - end. - -is_before_checkpoint(Time, CheckPoint) -> - calendar:datetime_to_gregorian_seconds( - calendar:now_to_datetime(CheckPoint)) - - calendar:datetime_to_gregorian_seconds(Time) > 0. - add_trusted_certs(Pid, Trustedcerts, Db) -> try ssl_pkix_db:add_trusted_certs(Pid, Trustedcerts, Db) diff --git a/lib/ssl/src/ssl_pem_cache.erl b/lib/ssl/src/ssl_pem_cache.erl new file mode 100644 index 0000000000..2b31374bcc --- /dev/null +++ b/lib/ssl/src/ssl_pem_cache.erl @@ -0,0 +1,266 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 20016-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%%---------------------------------------------------------------------- +%% Purpose: Manages ssl sessions and trusted certifacates +%%---------------------------------------------------------------------- + +-module(ssl_pem_cache). +-behaviour(gen_server). + +%% Internal application API +-export([start_link/1, + start_link_dist/1, + name/1, + insert/1, + clear/0]). + +% Spawn export +-export([init_pem_cache_validator/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-include("ssl_handshake.hrl"). +-include("ssl_internal.hrl"). +-include_lib("kernel/include/file.hrl"). + +-record(state, { + pem_cache, + last_pem_check :: erlang:timestamp(), + clear :: integer() + }). + +-define(CLEAR_PEM_CACHE, 120000). +-define(DEFAULT_MAX_SESSION_CACHE, 1000). + +%%==================================================================== +%% API +%%==================================================================== + +%%-------------------------------------------------------------------- +-spec name(normal | dist) -> atom(). +%% +%% Description: Returns the registered name of the ssl cache process +%% in the operation modes 'normal' and 'dist'. +%%-------------------------------------------------------------------- +name(normal) -> + ?MODULE; +name(dist) -> + list_to_atom(atom_to_list(?MODULE) ++ "dist"). + +%%-------------------------------------------------------------------- +-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}. +%% +%% Description: Starts the ssl pem cache handler +%%-------------------------------------------------------------------- +start_link(_) -> + CacheName = name(normal), + gen_server:start_link({local, CacheName}, + ?MODULE, [CacheName], []). + +%%-------------------------------------------------------------------- +-spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}. +%% +%% Description: Starts a special instance of the ssl manager to +%% be used by the erlang distribution. Note disables soft upgrade! +%%-------------------------------------------------------------------- +start_link_dist(_) -> + DistCacheName = name(dist), + gen_server:start_link({local, DistCacheName}, + ?MODULE, [DistCacheName], []). + + +%%-------------------------------------------------------------------- +-spec insert(binary()) -> {ok, term()} | {error, reason()}. +%% +%% Description: Cache a pem file and return its content. +%%-------------------------------------------------------------------- +insert(File) -> + {ok, PemBin} = file:read_file(File), + Content = public_key:pem_decode(PemBin), + case bypass_cache() of + true -> + {ok, Content}; + false -> + cast({cache_pem, File, Content}), + {ok, Content} + end. + +%%-------------------------------------------------------------------- +-spec clear() -> ok. +%% +%% Description: Clear the PEM cache +%%-------------------------------------------------------------------- +clear() -> + %% Not supported for distribution at the moement, should it be? + put(ssl_pem_cache, name(normal)), + call(unconditionally_clear_pem_cache). + +-spec invalidate_pem(File::binary()) -> ok. +invalidate_pem(File) -> + cast({invalidate_pem, File}). + +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +-spec init(list()) -> {ok, #state{}}. +%% Possible return values not used now. +%% | {ok, #state{}, timeout()} | ignore | {stop, term()}. +%% +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([Name]) -> + put(ssl_pem_cache, Name), + process_flag(trap_exit, true), + PemCache = ssl_pkix_db:create_pem_cache(), + Interval = pem_check_interval(), + erlang:send_after(Interval, self(), clear_pem_cache), + {ok, #state{pem_cache = PemCache, + last_pem_check = os:timestamp(), + clear = Interval + }}. + +%%-------------------------------------------------------------------- +-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}. +%% Possible return values not used now. +%% {reply, reply(), #state{}, timeout()} | +%% {noreply, #state{}} | +%% {noreply, #state{}, timeout()} | +%% {stop, reason(), reply(), #state{}} | +%% {stop, reason(), #state{}}. +%% +%% Description: Handling call messages +%%-------------------------------------------------------------------- +handle_call({unconditionally_clear_pem_cache, _},_, + #state{pem_cache = PemCache} = State) -> + ssl_pkix_db:clear(PemCache), + {reply, ok, State}. + +%%-------------------------------------------------------------------- +-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. +%% Possible return values not used now. +%% | {noreply, #state{}, timeout()} | +%% {stop, reason(), #state{}}. +%% +%% Description: Handling cast messages +%%-------------------------------------------------------------------- +handle_cast({cache_pem, File, Content}, #state{pem_cache = Db} = State) -> + ssl_pkix_db:insert(File, Content, Db), + {noreply, State}; + +handle_cast({invalidate_pem, File}, #state{pem_cache = Db} = State) -> + ssl_pkix_db:remove(File, Db), + {noreply, State}. + + +%%-------------------------------------------------------------------- +-spec handle_info(msg(), #state{}) -> {noreply, #state{}}. +%% Possible return values not used now. +%% |{noreply, #state{}, timeout()} | +%% {stop, reason(), #state{}}. +%% +%% Description: Handling all non call/cast messages +%%------------------------------------------------------------------- +handle_info(clear_pem_cache, #state{pem_cache = PemCache, + clear = Interval, + last_pem_check = CheckPoint} = State) -> + NewCheckPoint = os:timestamp(), + start_pem_cache_validator(PemCache, CheckPoint), + erlang:send_after(Interval, self(), clear_pem_cache), + {noreply, State#state{last_pem_check = NewCheckPoint}}; + +handle_info(_Info, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +-spec terminate(reason(), #state{}) -> ok. +%% +%% Description: This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any necessary +%% cleaning up. When it returns, the gen_server terminates with Reason. +%% The return value is ignored. +%%-------------------------------------------------------------------- +terminate(_Reason, #state{}) -> + ok. + +%%-------------------------------------------------------------------- +-spec code_change(term(), #state{}, list()) -> {ok, #state{}}. +%% +%% Description: Convert process state when code is changed +%%-------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +call(Msg) -> + gen_server:call(get(ssl_pem_cache), {Msg, self()}, infinity). + +cast(Msg) -> + gen_server:cast(get(ssl_pem_cache), Msg). + +start_pem_cache_validator(PemCache, CheckPoint) -> + spawn_link(?MODULE, init_pem_cache_validator, + [[get(ssl_pem_cache), PemCache, CheckPoint]]). + +init_pem_cache_validator([CacheName, PemCache, CheckPoint]) -> + put(ssl_pem_cache, CacheName), + ssl_pkix_db:foldl(fun pem_cache_validate/2, + CheckPoint, PemCache). + +pem_cache_validate({File, _}, CheckPoint) -> + case file:read_file_info(File, []) of + {ok, #file_info{mtime = Time}} -> + case is_before_checkpoint(Time, CheckPoint) of + true -> + ok; + false -> + invalidate_pem(File) + end; + _ -> + invalidate_pem(File) + end, + CheckPoint. + +is_before_checkpoint(Time, CheckPoint) -> + calendar:datetime_to_gregorian_seconds( + calendar:now_to_datetime(CheckPoint)) - + calendar:datetime_to_gregorian_seconds(Time) > 0. + +pem_check_interval() -> + case application:get_env(ssl, ssl_pem_cache_clean) of + {ok, Interval} when is_integer(Interval) -> + Interval; + _ -> + ?CLEAR_PEM_CACHE + end. + +bypass_cache() -> + case application:get_env(ssl, bypass_pem_cache) of + {ok, Bool} when is_boolean(Bool) -> + Bool; + _ -> + false + end. diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl index b4299969e4..961a555873 100644 --- a/lib/ssl/src/ssl_pkix_db.erl +++ b/lib/ssl/src/ssl_pkix_db.erl @@ -28,11 +28,11 @@ -include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.hrl"). --export([create/0, add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3, +-export([create/0, create_pem_cache/0, + add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3, extract_trusted_certs/1, remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1, ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2, - lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3, decode_pem_file/1, lookup/2]). %%==================================================================== @@ -52,13 +52,19 @@ create() -> %% on DER format to ssl:connect/listen.) ets:new(ssl_otp_cacertificate_db, [set, public]), %% Let connection processes call ref_count/3 directly - ets:new(ssl_otp_ca_file_ref, [set, public]), - ets:new(ssl_otp_pem_cache, [set, protected]), + {ets:new(ssl_otp_ca_file_ref, [set, public]), + ets:new(ssl_otp_ca_ref_file_mapping, [set, protected]) + }, + %% Lookups in named table owned by ssl_pem_cache process + ssl_otp_pem_cache, %% Default cache {ets:new(ssl_otp_crl_cache, [set, protected]), ets:new(ssl_otp_crl_issuer_mapping, [bag, protected])} ]. +create_pem_cache() -> + ets:new(ssl_otp_pem_cache, [named_table, set, protected]). + %%-------------------------------------------------------------------- -spec remove([db_handle()]) -> ok. %% @@ -70,6 +76,8 @@ remove(Dbs) -> true = ets:delete(Db1); (undefined) -> ok; + (ssl_otp_pem_cache) -> + ok; (Db) -> true = ets:delete(Db) end, Dbs). @@ -101,11 +109,6 @@ lookup_trusted_cert(_DbHandle, {extracted,Certs}, SerialNumber, Issuer) -> {ok, Cert} end. -lookup_cached_pem([_, _, PemChache | _], File) -> - lookup_cached_pem(PemChache, File); -lookup_cached_pem(PemChache, File) -> - lookup(File, PemChache). - %%-------------------------------------------------------------------- -spec add_trusted_certs(pid(), {erlang:timestamp(), string()} | {der, list()}, [db_handle()]) -> {ok, [db_handle()]}. @@ -122,17 +125,11 @@ add_trusted_certs(_Pid, {der, DerList}, [CertDb, _,_ | _]) -> add_certs_from_der(DerList, NewRef, CertDb), {ok, NewRef}; -add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache | _] = Db) -> - case lookup_cached_pem(Db, File) of - [{_Content, Ref}] -> +add_trusted_certs(_Pid, File, [ _, {RefDb, FileMapDb} | _] = Db) -> + case lookup(File, FileMapDb) of + [Ref] -> ref_count(Ref, RefDb, 1), {ok, Ref}; - [Content] -> - Ref = make_ref(), - update_counter(Ref, 1, RefDb), - insert(File, {Content, Ref}, PemChache), - add_certs_from_pem(Content, Ref, CertsDb), - {ok, Ref}; undefined -> new_trusted_cert_entry(File, Db) end. @@ -151,25 +148,6 @@ extract_trusted_certs(File) -> {error, {badmatch, Error}} end. -%%-------------------------------------------------------------------- -%% -%% Description: Cache file as binary in DB -%%-------------------------------------------------------------------- --spec cache_pem_file(binary(), [db_handle()]) -> {ok, term()}. -cache_pem_file(File, [_CertsDb, _RefDb, PemChache | _]) -> - {ok, PemBin} = file:read_file(File), - Content = public_key:pem_decode(PemBin), - insert(File, Content, PemChache), - {ok, Content}. - - --spec cache_pem_file(reference(), binary(), [db_handle()]) -> {ok, term()}. -cache_pem_file(Ref, File, [_CertsDb, _RefDb, PemChache| _]) -> - {ok, PemBin} = file:read_file(File), - Content = public_key:pem_decode(PemBin), - insert(File, {Content, Ref}, PemChache), - {ok, Content}. - -spec decode_pem_file(binary()) -> {ok, term()}. decode_pem_file(File) -> case file:read_file(File) of @@ -246,6 +224,8 @@ select_cert_by_issuer(Cache, Issuer) -> %%-------------------------------------------------------------------- ref_count({extracted, _}, _Db, _N) -> not_cached; +ref_count(Key, {Db, _}, N) -> + ref_count(Key, Db, N); ref_count(Key, Db, N) -> ets:update_counter(Db,Key,N). @@ -278,9 +258,9 @@ insert(Key, Data, Db) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -update_counter(Key, Count, Db) -> - true = ets:insert(Db, {Key, Count}), - ok. +init_ref_db(Ref, File, {RefDb, FileMapDb}) -> + true = ets:insert(RefDb, {Ref, 1}), + true = ets:insert(FileMapDb, {File, Ref}). remove_certs(Ref, CertsDb) -> true = ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}), @@ -326,10 +306,10 @@ decode_certs(Ref, Cert) -> undefined end. -new_trusted_cert_entry(File, [CertsDb, RefDb, _ | _] = Db) -> +new_trusted_cert_entry(File, [CertsDb, RefsDb, _ | _]) -> Ref = make_ref(), - update_counter(Ref, 1, RefDb), - {ok, Content} = cache_pem_file(Ref, File, Db), + init_ref_db(Ref, File, RefsDb), + {ok, Content} = ssl_pem_cache:insert(File), add_certs_from_pem(Content, Ref, CertsDb), {ok, Ref}. @@ -361,4 +341,3 @@ crl_issuer(DerCRL) -> CRL = public_key:der_decode('CertificateList', DerCRL), TBSCRL = CRL#'CertificateList'.tbsCertList, TBSCRL#'TBSCertList'.issuer. - diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl index 8245801139..05a7aaaa82 100644 --- a/lib/ssl/src/ssl_sup.erl +++ b/lib/ssl/src/ssl_sup.erl @@ -25,7 +25,7 @@ -behaviour(supervisor). %% API --export([start_link/0, manager_opts/0]). +-export([start_link/0]). %% Supervisor callback -export([init/1]). @@ -44,90 +44,28 @@ start_link() -> %%%========================================================================= init([]) -> - SessionCertManager = session_and_cert_manager_child_spec(), - TLSConnetionManager = tls_connection_manager_child_spec(), - %% Handles emulated options so that they inherited by the accept - %% socket, even when setopts is performed on the listen socket - ListenOptionsTracker = listen_options_tracker_child_spec(), - - DTLSConnetionManager = dtls_connection_manager_child_spec(), - DTLSUdpListeners = dtls_udp_listeners_spec(), + {ok, {{rest_for_one, 10, 3600}, [ssl_admin_child_spec(), + ssl_connection_sup() + ]}}. - {ok, {{one_for_all, 10, 3600}, [SessionCertManager, TLSConnetionManager, - ListenOptionsTracker, - DTLSConnetionManager, DTLSUdpListeners - ]}}. - - -manager_opts() -> - CbOpts = case application:get_env(ssl, session_cb) of - {ok, Cb} when is_atom(Cb) -> - InitArgs = session_cb_init_args(), - [{session_cb, Cb}, {session_cb_init_args, InitArgs}]; - _ -> - [] - end, - case application:get_env(ssl, session_lifetime) of - {ok, Time} when is_integer(Time) -> - [{session_lifetime, Time}| CbOpts]; - _ -> - CbOpts - end. - %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - -session_and_cert_manager_child_spec() -> - Opts = manager_opts(), - Name = ssl_manager, - StartFunc = {ssl_manager, start_link, [Opts]}, +ssl_admin_child_spec() -> + Name = ssl_admin_sup, + StartFunc = {ssl_admin_sup, start_link, []}, Restart = permanent, Shutdown = 4000, - Modules = [ssl_manager], - Type = worker, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - -tls_connection_manager_child_spec() -> - Name = tls_connection, - StartFunc = {tls_connection_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_connection_sup], + Modules = [ssl_admin_sup], Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. -dtls_connection_manager_child_spec() -> - Name = dtls_connection, - StartFunc = {dtls_connection_sup, start_link, []}, +ssl_connection_sup() -> + Name = ssl_connection_sup, + StartFunc = {ssl_connection_sup, start_link, []}, Restart = permanent, Shutdown = 4000, - Modules = [dtls_connection_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - -listen_options_tracker_child_spec() -> - Name = tls_socket, - StartFunc = {ssl_listen_tracker_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_socket], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - -dtls_udp_listeners_spec() -> - Name = dtls_udp_listener, - StartFunc = {dtls_udp_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [], + Modules = [ssl_connection_sup], Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. -session_cb_init_args() -> - case application:get_env(ssl, session_cb_init_args) of - {ok, Args} when is_list(Args) -> - Args; - _ -> - [] - end. diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index d85be6c69e..e14f7f60c4 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -172,8 +172,8 @@ revoke(Root, CA, User, C) -> gencrl(Root, CA, C). gencrl(Root, CA, C) -> - %% By default, the CRL is valid for 24 hours from now. - gencrl(Root, CA, C, 24). + %% By default, the CRL is valid for a week from now. + gencrl(Root, CA, C, 24*7). gencrl(Root, CA, C, CrlHours) -> CACnfFile = filename:join([Root, CA, "ca.cnf"]), diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index de5895d7ba..f0a3c42e8d 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -961,9 +961,9 @@ clear_pem_cache(Config) when is_list(Config) -> {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), [_, _,_, _, Prop] = StatusInfo, State = ssl_test_lib:state(Prop), - [_,FilRefDb |_] = element(6, State), + [_,{FilRefDb, _} |_] = element(6, State), {Server, Client} = basic_verify_test_no_close(Config), - CountReferencedFiles = fun({_,-1}, Acc) -> + CountReferencedFiles = fun({_, -1}, Acc) -> Acc; ({_, N}, Acc) -> N + Acc diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl index 21989f8d99..70fd0af9b4 100644 --- a/lib/ssl/test/ssl_bench_SUITE.erl +++ b/lib/ssl/test/ssl_bench_SUITE.erl @@ -88,7 +88,6 @@ end_per_testcase(_Func, _Conf) -> -define(FPROF_SERVER, false). -define(EPROF_CLIENT, false). -define(EPROF_SERVER, false). --define(PERCEPT_SERVER, false). %% Current numbers gives roughly a testcase per minute on todays hardware.. @@ -190,7 +189,6 @@ server_init(ssl, setup_connection, _, _, Server) -> ?FPROF_SERVER andalso start_profile(fprof, [whereis(ssl_manager), new]), %%?EPROF_SERVER andalso start_profile(eprof, [ssl_connection_sup, ssl_manager]), ?EPROF_SERVER andalso start_profile(eprof, [ssl_manager]), - ?PERCEPT_SERVER andalso percept:profile("/tmp/ssl_server.percept"), Server ! {self(), {init, Host, Port}}, Test = fun(TSocket) -> ok = ssl:ssl_accept(TSocket), @@ -247,7 +245,6 @@ setup_server_connection(LSocket, Test) -> receive quit -> ?FPROF_SERVER andalso stop_profile(fprof, "test_server_res.fprof"), ?EPROF_SERVER andalso stop_profile(eprof, "test_server_res.eprof"), - ?PERCEPT_SERVER andalso stop_profile(percept, "/tmp/ssl_server.percept"), ok after 0 -> case ssl:transport_accept(LSocket, 2000) of @@ -388,13 +385,6 @@ start_profile(fprof, Procs) -> fprof:trace([start, {procs, Procs}]), io:format("(F)Profiling ...",[]). -stop_profile(percept, File) -> - percept:stop_profile(), - percept:analyze(File), - {started, _Host, Port} = percept:start_webserver(), - wx:new(), - wx_misc:launchDefaultBrowser("http://" ++ net_adm:localhost() ++ ":" ++ integer_to_list(Port)), - ok; stop_profile(eprof, File) -> profiling_stopped = eprof:stop_profiling(), eprof:log(File), diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl index f10d27fbc6..96b15d9b51 100644 --- a/lib/ssl/test/ssl_pem_cache_SUITE.erl +++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl @@ -82,8 +82,8 @@ pem_cleanup() -> [{doc, "Test pem cache invalidate mechanism"}]. pem_cleanup(Config)when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_opts, Config), - ServerOpts = proplists:get_value(server_opts, Config), + ClientOpts = proplists:get_value(client_verification_opts, Config), + ServerOpts = proplists:get_value(server_verification_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = |