pax_global_header00006660000000000000000000000064143735023400014513gustar00rootroot0000000000000052 comment=b972b8ce386c29bcbcd45029a617db3db9e5b6ca tuna-0.19/000077500000000000000000000000001437350234000124135ustar00rootroot00000000000000tuna-0.19/ChangeLog000066400000000000000000000156201437350234000141710ustar00rootroot00000000000000* Fri Feb 1 2013 Arnaldo Carvalho de Melo - 0.10.4-1 63580be procview: Allow configuring a single thread Fixes https://bugzilla.redhat.com/show_bug.cgi?id=601234 5d4ee70 tuna: Tighten "No such process" schedutils exception handling cca561d tuna: Catch OSError exceptions from python-schedutils Fixes https://bugzilla.redhat.com/show_bug.cgi?id=871598 07907b3 tuna: Add man page for command line * Fri Aug 24 2012 Arnaldo Carvalho de Melo - 0.10.3-1 . Fix exception on ARM . Remove raw cycles column when perf events are used . Don't touch the migration threads . Update testuna to work with 3.6.0-rc1 * Thu Jul 28 2011 Arnaldo Carvalho de Melo - 0.10.2-1 [acme@felicio tuna]$ git log v0.10.1.. --format=oneline --abbrev-commit ddd2754 oscilloscope: Switch from "Bistream Vera Sans" to "Liberation Sans" c5c558c oscilloscope: Switch from Numeric to numpy 28d87b6 MANIFEST: Add tuna/oscilloscope.py 4de7144 cmdline: Fix IRQ affinity setting 40bc92d procview gui: Load perf globally * Wed Feb 23 2011 Arnaldo Carvalho de Melo - 0.10.1-1 24f19ce procview gui: Don't assume the perf binding is always available * Wed Feb 23 2011 Arnaldo Carvalho de Melo - 0.10-1 [acme@emilia tuna]$ git log v0.9.4.. --format=oneline --abbrev-commit 3b54abd perf: Create the threads pidstat member when we notice the first thread 04f26d1 gui: Fixup perf.evlist constructor call 00a2aa4 gui: Use perf if available f130d84 cmdline: Alert the user when GUI packages are not installed * Mon May 10 2010 Arnaldo Carvalho de Melo - 0.9.3-1 [acme@mica tuna]$ git log v0.9.2.. --format=oneline --abbrev-commit 7d4f258 cmdline: Emit error when affinity operation can't be performed a9e68d1 cmdline: Use tuna.cpustring_to_list in --cpus 21de19e tuna: Make is_hardirq_handler handle non thread group leaders ab01211 cmdline: Handle unknown schedulers in -p/--priority: bbdaf5a cmdline: Improve message about thread list being needed by -p/--priority 427f94c cmdline: Fix --priority error message when no threads are informed 85bb5d7 cmdline: Fix help line entry for --priority 6d0288b tuna: Make cpustring_to_list understand 0x prefixed hexadecimal numbers 7210b53 gui: fix use of cpu ranges f2bc415 irqview: Fix some race conditions a3e85fe cmdline: On threaded irq kernels, map irqs to threads 7d33dd1 cmdline: Fix threaded interrupt name bug 3a71dae cmdline: --priority/-p requires a thread list [acme@mica tuna]$ * Thu Nov 12 2009 Arnaldo Carvalho de Melo - cmdline: -Q --show_irqs shows IRQs, so that we can see its affinity masks even on non-threaded IRQ kernels - tuna: Ignore non-existing threads in threads_set_priority - tuna: i18n, Chinese * Thu Sep 3 2009 Arnaldo Carvalho de Melo - 0.9.1-1 - cmdline: --socket needs an argument - tuna: Present more graceful failure mode when machine can't start gui - tuna: Handle 2.6.31 style threaded irq names * Wed May 13 2009 Arnaldo Carvalho de Melo - 0.9-1 - tuna: i18n, pt_BR & Japanese - oscilloscope: Fix scaling bugs and add --sample_multiplier - oscilloscope: properly refresh all widgets - oscilloscope: Remove needless shebang and reword the package summary - cmdline: add --show_sockets/-n * Mon Oct 27 2008 Arnaldo Carvalho de Melo - 0.8.4-1 - cmdline: Allow globbing --irqs/-q - cmdline: Allow globbing --threads/-t - cmdline: Support regex on --thread - cmdline: Check if thread names resolved to TIDs - irqview: We have to pass an int to schedutils.setscheduler - irqview: Initialize the rt_prio entry box - gui: move procview to a separate file - gui: Move irqview to a separate file - gui: remove the Set/set trick in tuna/tuna_gui.py, not used anymore - gui: Use schedutils.SCHED_{OTHER,FIFO,RR} - gui: move cpuview classes to a separate file - gui: Stop handling exceptions at procview.set_thread_columns - oscilloscope: Ignore invalid samples, emitting just a warning * Mon Oct 27 2008 Arnaldo Carvalho de Melo - 0.8-1 - [Non]VolCtxtSwitch columns should be represented in the GUI as unsigned - Several spelling fixes - Handle some more schedutils exceptions - Fix getopt long option for --priority, it requires an arg - Set the irq affinity mask when setting a IRQ thread affinity - Add a --spread command line option, it will spread the threads passed thru --threads into the cpus specified in --cpu or --socket. * Mon Sep 15 2008 Arnaldo Carvalho de Melo - 0.7-1 - CPU topology support: operations now can be performed on CPU sockets in addition to individual CPUs - --show_threads: command line now can see the same information available in the GUI * Tue Aug 12 2008 Arnaldo Carvalho de Melo - 0.6-1 - tuna: posix_cpu_timer is percpu but its too long to have '/' in the cmdline - tuna: Fixup the message about what filename was really used (rtgroups) - tuna: Save the affinity mask for non-percpu kthreads - tuna: Ignore rtprio when changing sched policy to SCHED_OTHER * Thu Aug 7 2008 Arnaldo Carvalho de Melo - 0.5-1 - tuna_gui: Provide instructions on how to use the generated rtctl file - tuna_gui: Add "Save kthreads tunings" menu entry in the process list box - tuna: Implement saving current kthread sched policy and rtprio as an rtctl file - help: Add more kernel thread help texts, written by the MRG crew * Tue Jun 17 2008 Arnaldo Carvalho de Melo - 0.4-1 - oscilloscope subpackage - oscilloscope: Allow passing the number of samples to appear on screen - oscilloscope: use io_add_watch instead of timeout_add - oscilloscope: check if the latency tracer is available - oscilloscope: Allow disabling auto-scaling - oscilloscope: group the system info and help frames in a vbox - oscilloscope: parse X geometry parameter - tuna: Convert widget coords to bin_window coords - tuna: Implement --affect_children and --priority * Fri May 16 2008 Arnaldo Carvalho de Melo - 0.3-1 - Add oscilloscope command, initially useful with signaltest and cyclictest, but will also be used with the latencytest utility in the qpid project and with any other source of signals. Requires python-matplotlib, that will be added to the MRG repo soon. - Allow toggling auto-refresh from the irq and threads views - Changes to make tuna work on older RHEL versions, helpful when evaluating RHEL-RT components. - Allow using tuna without GUI libraries installed, please see: tuna --help For available commands. - Several fixes * Thu Mar 27 2008 Arnaldo Carvalho de Melo - 0.2-1 - Command line interface - Remove the requirement of a GUI packages - Allow moving one child thread to a CPU - Status icon - "What is this?", for now just for some kernel threads - Add "Restore CPU" to undo "Isolate CPU" - Faster CPU isolation process - Allow moving IRQs & Threads to all cpus - CPU filtering * Mon Feb 26 2008 Arnaldo Carvalho de Melo - 0.1-1 - package created tuna-0.19/MANIFEST000066400000000000000000000007611437350234000135500ustar00rootroot00000000000000help/kthreads/ oscilloscope-cmd.py tuna-cmd.py tuna/__init__.py tuna/help.py tuna/sysfs.py tuna/oscilloscope.py tuna/tuna.py tuna/config.py tuna/tuna_gui.py tuna/tuna_gui.glade tuna/gui/__init__.py tuna/gui/cpuview.py tuna/gui/irqview.py tuna/gui/procview.py tuna/gui/commonview.py tuna/gui/profileview.py tuna/gui/util.py setup.py rpm/SPECS/tuna.spec MANIFEST po/LINGUAS po/POTFILES.in po/ja.po po/pt_BR.po po/zh_CN.po ChangeLog docs/oscilloscope+tuna.html docs/oscilloscope+tuna.pdf docs/tuna.8 tuna-0.19/Makefile000066400000000000000000000026061437350234000140570ustar00rootroot00000000000000PACKAGE := tuna VERSION := $(shell rpm -q --qf '%{VERSION} ' --specfile rpm/SPECS/$(PACKAGE).spec | cut -d' ' -f1) rpmdirs: @[ -d rpm/BUILD ] || mkdir rpm/BUILD @[ -d rpm/RPMS ] || mkdir rpm/RPMS @[ -d rpm/SRPMS ] || mkdir rpm/SRPMS @[ -d rpm/SOURCES ] || mkdir rpm/SOURCES bz2: rpmdirs git archive --format=tar --prefix=$(PACKAGE)-$(VERSION)/ HEAD | \ bzip2 -9 > rpm/SOURCES/$(PACKAGE)-$(VERSION).tar.bz2 rpm: bz2 rpmdirs rpmbuild -ba --define "_topdir $(PWD)/rpm" rpm/SPECS/$(PACKAGE).spec bz2dev: rpmdirs @mkdir -p /tmp/$(PACKAGE)-$(VERSION) @tar cf - `cat MANIFEST` | (cd /tmp/$(PACKAGE)-$(VERSION) ; tar xf -) @(cd /tmp; tar cf - $(PACKAGE)-$(VERSION)) | bzip2 -9 > rpm/SOURCES/$(PACKAGE)-$(VERSION).tar.bz2 rpmdev: bz2dev rpmdirs rpmbuild -ba --define "_topdir $(PWD)/rpm" rpm/SPECS/$(PACKAGE).spec po/$(PACKAGE).pot: xgettext -k_ -kN_ -f po/POTFILES.in -o $@ po/%.po: po/$(PACKAGE).pot msgmerge --suffix=.old -U $@ $< && rm -f $@.old rpmclean: @rm -f rpm/RPMS/*/$(PACKAGE)-$(VERSION)-*.rpm @rm -f rpm/SRPMS/$(PACKAGE)-$(VERSION)-*.src.rpm @rm -f rpm/SOURCES/$(PACKAGE)-$(VERSION).tar.bz2 @rm -rf rpm/BUILD/$(PACKAGE)-$(VERSION)* pyclean: @find . -type f \( -name \*~ -o -name \*.pyc \) -delete .PHONY: tags tags: ctags -R --extras=+fq --python-kinds=+cfmvi .PHONY: cleantags cleantags: rm -f tags .PHONY: cleanlogs cleanlogs: rm -rf tuna-20* clean: pyclean rpmclean tuna-0.19/docs/000077500000000000000000000000001437350234000133435ustar00rootroot00000000000000tuna-0.19/docs/oscilloscope+tuna.html000066400000000000000000000315401437350234000176750ustar00rootroot00000000000000 You can tune a piano - and you can tuna Linux system, too!: OSADL - Open Source Automation Development Lab eG
Open Source Automation Development Lab

2008-07-27 12:00 Age: 351 Days

You can tune a piano - and you can tuna Linux system, too!

By: Carsten Emde (Used by permission)

New versatile tools are helping us to manage real-time challenges under Linux (but you still can't tuna fish).

The "CyclictestoSCOPE" in action

Do you wish you had a built-in oscilloscope that continuously displays the latency of your system? Here is one - thanks to Arnaldo Carvalho de Melo who not only wrote the oscilloscope but also the graphical process inspection and modification tool tuna that is part of the same tool package. In addition, these tools are not only very versatile tools that help us to manage real-time challenges - they also are excellent examples of the power and the beauty of the Python script language and its libraries.

However, before we can install and run these tools, we must learn a little bit about cyclictest and its command line arguments, since this is where oscilloscope gets its data from.

Cyclictest

Cyclictest was initially written by Thomas Gleixner as an in-house tool to investigate possible regressions while developing the RT Preempt patches. Today, it is the state-of-the-art measurement tool to determine the internal worst-case latency of a Linux real-time system. No new version of the RT Preempt patches is ever released without having run cyclictest successfully on the new kernel. The git tree that contains the cyclictest tool can be cloned

KERNELGIT=git://git.kernel.org/pub/scm/linux/kernel/git
git clone $KERNELGIT/clrkwllms/rt-tests.git

but the software is also available as tarball. On an RPM package system such as Redhat Enterprise Linux or Fedora, simply type

cd rt-tests
make release
mv rt-tests-*.tar.gz releases
make rpm
rpm -Uvh RPMS/i386/rt-tests-0*.i386.rpm

to build and install it. Mandatory arguments of cyclictest are -n (use clock_nanosleep) and -p99 (RT priority) on a uniprocessor and additional -a -t (one thread per CPU each on its own processor) on a multi-processor system. If in doubt, the -m option may be used to prevent the cyclictest memory from being paged to the swap area. In order to run as many consecutive threads as possible and to achieve the highest probability to meet a latency, the thread interval (-i option) should be about twice as long as the expected worst-case latency, and the thread delay (-d option) should be set to the interval divided by the number of CPUs. Here is an example of a cyclictest run on a Core 2 Duo, 64-bit, 2.4-GHz board:

# cyclictest -a -t -n -p99 -i100 -d50
560.44 586.11 606.12 211/1160 3727
T: 0 (18617) P:99 I:100 C:1011846111 Min:  2 Act:  4 Avg:  5 Max: 39
T: 1 (18618) P:98 I:150 C: 708641019 Min:  2 Act:  5 Avg: 11 Max: 57

The test was running on a linux-2.6.24.7-rt15 kernel in parallel with repeated loops of "hackbench 25","ls -Ral /", and flood ping from outside. After having executed more than one billion cyclictest loops, the internal worst-case latency is still only 39 s!

Tuna

The tuna tool is downloaded from the git repository and installed using the command sequence (updated on February 2, 2009):

KERNELGIT=git://git.kernel.org/pub/scm/linux/kernel/git
git clone $KERNELGIT/acme/python-linux-procfs.git
cd python-linux-procfs
make rpm
rpm -Uvh rpm/RPMS/noarch/python-linux-procfs-*.noarch.rpm
cd -
git clone $KERNELGIT/acme/tuna.git
cd tuna
make rpm
rpm -Uvh rpm/RPMS/noarch/tuna-*.noarch.rpm \
rpm/RPMS/noarch/oscilloscope-*.noarch.rpm

Start tuna simply by running it in an X Window environment. Now, what can you get from tuna? See tuna in actionFirst, the output is very similar to the output of the well-known command line tool top. In addition, tuna has a much easier interface to arrange the display, to show threads etc. - tuna's CU optionsbut most importantly, it lets you modify many process settings such as the scheduling priority and policy (click on the right mouse button while the cursor is above one of the process lines). It also has a special menu (click on the right mouse button while the cursor is above the CPU Usage frame) to include or isolate the available processors.

Oscilloscope

Finally, here comes the oscilloscope tool. It uses cyclictest's special -v option that causes the data to be continuously written to standard output path so they can be evaluated by another program connected via pipe. In this case, we can only run a single thread and, unfortunately, we cannot use the -i option explained above, since the oscilloscope has only a limited capacity (will be covered later). To get a first impression, run oscilloscope with the following command:

cyclictest -t1 -n -p99 -v | oscilloscope >/dev/null

Cool, isn't it? You may even increase the number of samples on screen using the -s option such as

cyclictest -t1 -n -p99 -v | oscilloscope -s1000 >/dev/null

The GUI of the oscilloscopeUnfortunately, this will not capture every possible latency, since the cyclictest thread only runs once per millisecond. If we run it faster, for example by specifying -i100, our oscilloscope will be unable to follow. We have, therefore, implemented cyclictest's -o option (oscilloscope mode). This option lets cyclictest reduce the output by the given factor and write only the maximum value that occurred during the specified period of time. The command

cyclictest -t1 -n -p99 -i100 -o10 -v | oscilloscope -s1000 >/dev/null

will then probably be able to provide a much better picture of the worst-case latency of a given system - as a disadvantage, however, the statistics given in the oscilloscope display are incorrect: The numbers no longer reflect the original raw samples but are based on preprocessed reduced data. However, since this is in any case a more pessimistic view, there is no danger that we underestimate the worst-case latency of the system.


Online Article: http://www.osadl.org/Single-View.111+M5d6d15531f8.0.html
tuna-0.19/docs/oscilloscope+tuna.pdf000066400000000000000000004200551437350234000175050ustar00rootroot00000000000000%PDF-1.4 % 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream x\[6~ׯhnda!,6!䡏t $JU%ϙLqۺRI>_w)8-Z鲮f:LM~Qfqtӏ=k6vm5$/<|aM?z+L_L:asmvvk3e-{} u /ӧ~_TWi8f?,|t)o/,s\\X7&1+wڬ Gݴ=ԩ+(p.7yM\} vw1q^Cm|U~ %/nmefKbJ8) #7ɄFﲷmz4mLCH|~nrɖb'$F\ ؚ;{)$9u4e(hRUAMS6)'?MmL`c,Fn6$ e#˶ hR٠Y01bm]E˼ش:7DRH G0do._EN5٠Iz0[mgkŠȦnQŸ c.hxm;3`ܦpWLe`2x<| .i'YAL-%^gc2 RKFn) e͵m-H FQKSK2[fW?@]vQLr, <ɝ4؄N+Y6] [L\TD5^j*FF۫BLܒxJgJho Φ0mKyj$"QnܠV ǰ8QRG2`4x+d .| `\w;ͣIPdL ԡ9uSNٛMNqIٵv)^G>l0IH!SZDiWY~jMDއ3wgvRR<GrളyN0{\|qJbv U[na3w$*b`J^KDߍR=`$!Z(^R2 2"/Y{`fzֈ |Cd@h TH^dUpoll-yۗbې0Kޑ3\oEtBd q<@`840[$`8[$VAQ?KO hV/ i^gCveL6eIP!R8[(<{wey>w?ŎNM+P:jk\⺐gy];z>5!ƛ}G ]kFQ "!, IV%1 Mnot8[a?}T"NYm@C9;FCk-cY WhdPôG.kiDH ;ӣ<>GUs jQPƈWf%fT4 P3Vȵzq.Ko&d `fP*$4#g2z̖X` *d@HIz.EåMA\,8(NF3$BOCQqGGSX\>4PDeFPXir]AXp` S ,%â҃d5:,P ޹caц!@"ǚG3lCa @.$UHE2ذ-bm6D/Q:Jǎb* uݍW xvp(Fn䤭>h>[@up+'dtEHgQbF0ObX=Y~}$y&Xס[e` w]0DT3Šа&r5j]e|r/!1X볅` `فϨDqC>S, xN )[z.\f_:VUdg,Q1R]ѧhu.4| HOd;.eZ?"FvAq=7=|,5d긤Agݨ*7J{)?SM^0-fa *' )$ V L©YQ0~ym~1C' eWT+T%F-iٶy1./|v:{cY#wșyzqk\w<.c a ghdܔ3Lx0L3c4 D`bDa`|ƣ_%m\i?!uMKz*V˟lɖҿ;=fl`7$cyb;x௳W+&Q "l@՝M7F U," m+W;|gK\!;̮s$Lb{~UAe1_)5qK\D ϶l!tFl64ፕhaC909T , PJەuI!Thus$v$㢦,ˎHܝqX"7Ċ,9?N w=? U+ANŽS(ӧHg1#x ޝ, ]І>ZZ<0-]?K[C"pQU ٣ ̉9> D]Hc4Zf+npG"BI decy>qъ%ƺ'WS3|,A |!P]gV"j?d+RF=hMwP£:Z=;(U^:Smp\Q-|Gfe[4ȝ`Gte=Ʌ3wuMysUEy:# *b7Q՝]doeP]vAZXPE€eo~TuNś$Vy8}f)T?R܉|v"Z|Di~:{Y#"0,8<;(h8pط˻3<; R쪹GA\kpj4P -w 2=l=ԘmAml's,œ 8(BmzёWx8K"t3 ػ Jx.lL;7yGh#AZ#% rl ~5/s }R~%V!fW ~ ˔ɻ^lOZ_uzBiNwȑ endstream endobj 4 0 obj 4194 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Pattern << /p5 5 0 R /p7 7 0 R >> /Font << /f-0-0 6 0 R /f-1-0 8 0 R /f-2-0 9 0 R /f-3-0 10 0 R /f-4-0 11 0 R >> >> endobj 12 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 612 792 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /CS /DeviceRGB >> /Resources 2 0 R >> endobj 13 0 obj << /Length 14 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 160 /Height 70 /ColorSpace /DeviceGray /BitsPerComponent 8 >> stream x{PU W|h s4iP41MLGD|6R`k2Q*K3H,&i.tΞsv]δ?s~9XSL2eʔkhQ!4*@oHj:(\Ga.xI=w0VMrbTdcէL߼ކÚTtlŖPibp)oE0ʼnjܜ}s/ Tli5%OO=V|ü0OC/!O_ͶI>4v˜M=nKA:k޶.Q|qXÜv."R󊝨wowHş G-|E}]+uj_  + U#u̇vNՍRސrOéTqA7ׯ{³˳$x#G*îqZ| >fn *QgJ^/kɇ;K͒VхwڿF/GI6* 17kt e[Ұ< }Xř?L(ӴE(\wHra7B#fɓ#i Y1ͻ0-{Hu,io%6aMv`0΅Ҩ׃  djO*Dzn#8si,;Y' .`!z2@M,'8kl_^/E ˡ}I.#囝Ifb4wKUcQ Dh|#CC7[sP!A$)^!7hc%4w>GW|X.VϷۨ;DrIm-7[O! > mF}I';d|.o1%ѲCY)[5Jhp^D#͉&r~nWP{|P8_Ko(Ȥ$Ԏ1 lZa=a| E{ {j !vR',YlП7$9Et> SGI3R[JRWWU/_[4vQ<.5-H$X}0e_4JXw|闔'o/郅Umm Y'7e(,>09f `Ȩh4.kaO$-5M\j+oUmuaZwG+zlϧ2X}zi3o6$ݼ~.՜h ΊEzeE4'S'x Krd,Ar'*izr=&2=mcjۚ [(=P _P{ ]}Nt'|zr)C1Gx ks ~z:WOϲ)U %}(ct. ]|^o3+߰pbsYXg/7K:fw/O;gʔ)SL?tG endstream endobj 14 0 obj 1673 endobj 15 0 obj << /Length 16 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 160 /Height 70 /ColorSpace /DeviceRGB /BitsPerComponent 8 /SMask 13 0 R >> stream x\sFi`B Rj/!4jg }J?xZ 9uێ߁$r`~uÊq))nlcn->=>bo@`Jho w4$_+Al`QoKpJŹww75B 9s=R'GBL;s}A +'$'BKKV'* ^6YP#v0/l TvL,jO/n!qyk򣌕jQOLl_Wr$TQi s'(b{~D fI~C)Ȝ*Ik%˲F rxBlqCŻ=jѠ}p_Ҙ%P)P>*k5"bUaVTDsJ$ *Z\+~qr@Udj=&*f8+o=_tqB/?C7#uHNP4U3o[!a%5/ێnvoOį;C{C|w|ض-Ѹh۟wB.WW':kR_@Ó&"I9gEk*L43p7#R8 %/깣o P0ֺ3VBW%&  U:Ԗ$و)rq:OOShBZ?$.B)=l>9}2cax#tw rfyq0R͕︳#X \[ )s>& wE[Qv߆EJ$[8)Vi0އjǿo"U{z~ X\بGs i!D.e_c;z|W1-[dc.wk~ _xY(>?0*!O3S(&~鐃5O㷒+>99<'rcþLBmb=>4ڽ~B ď5FQ^@|tto&8l9vYw/4>T)q6fGqTJr-jhhhhhhhhhhhhhhhhhhhhhh/m endstream endobj 16 0 obj 2192 endobj 5 0 obj << /Length 17 0 R /PatternType 1 /BBox [0 0 160 70] /XStep 2356 /YStep 2356 /TilingType 1 /PaintType 1 /Matrix [ 0.660573 0 0 0.660573 55.761529 680.089735 ] /Resources << /XObject << /x15 15 0 R >> >> >> stream q 160 0 0 70 0 0 cm /x15 Do Q endstream endobj 17 0 obj 32 endobj 18 0 obj << /Length 19 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 144 /Height 107 /ColorSpace /DeviceRGB /BitsPerComponent 8 >> stream x\wT$dY Yc!q r. $$a d!I.UGǥbZkOmm˗L /r~'{k҉iXN/_اBh¨哿;a_z_cD˯BhԂK- hW@Z^jN`8BUp!^jAFMhԂ·jA^jAt^jACR P˪ެNcdzy*=@ޖ4{ . o1j7 ^*/䠗x˹a\^oԖ\ '0\2Gthr+whڲA7z^:3ԯ]Z5z W Z@CD)pM|3L&b|t^jACR .ihW 3Q+ڒ%F* rs3Hׄ ҙhJg HyҙȄjF~T_onU W Dl%*^8Azl+D#^&Jg"ͅҙȄjzM>I:T@zM1Y:T@zL4R5et&:wҙȄjҙȄjzh!FRdm"fϔNF#)I{gK'99WQgt2I% E o*.N&3(=R蕷5;G:̠Xer4F_\d2[VH12#~k^_t"d2aߧ}Bd2"%/N&KNEI'ҙX D&mKM76p.kc2u×Ig"'CgV۵u^KY*mPTH1 ]+X/0K2%y? ~k^]&$~6}k,*=c\z-ltg(xvwk/BȀضl{ƭ/zF6h#mo}?ɱ;W>ҲC׊/r+wlwűNUA{?ܻ(HFND|i{|Ҥ7h(.m&rL_5`ԁ0<ͳ׌c :tk@ï_ ÊjߩumvjVǬc>f[5oEG=DikY<݀ߎ^ `e-Bn;oXB~/(Ϗ{D>2|sJv y/Vͫ{資_"3{wd η hmk뒇Pڄsw|Zgʯ˟:x uIz^2$in r^𝙤,g09dOѡi8`%(H٘KY _+@%N>UGxw@7ptĉM 7'wDLBoNvѫ~YMkB q.ؿ47}&g+qF@zl{`=x; 6h.ӰR3U:@Єdd,06[Ƌ4Z̎dų'ȼwLx5XF~$ wjpx\<3Guۋ6vn/?ZzIB䁾OHaZQLhD_@C8 K/u잞+bBLamlB`DYUHP|G>w}g'Gs0>;]׮,ޚHD oHJ0# |}uoq݊ *F+Z`<}=M{ QaZ)4-W톩:.8g ~eWuLa! Y}`"z?%ΐ UroƍOL|ȃ Ì-j5\  Lm &Wl( s8Ǡ7"OvJm3@?FC P\I!XR!^8l@p8OQP# tu`;_?"'Ad> 2GXսhb]GV"Vg4-]U1߾4Fnii“ endstream endobj 19 0 obj 2872 endobj 7 0 obj << /Length 20 0 R /PatternType 1 /BBox [0 0 144 107] /XStep 2377 /YStep 2377 /TilingType 1 /PaintType 1 /Matrix [ 0.660573 0 0 0.660573 437.341732 491.187772 ] /Resources << /XObject << /x18 18 0 R >> >> >> stream q 144 0 0 107 0 0 cm /x18 Do Q endstream endobj 20 0 obj 33 endobj 22 0 obj << /Length 23 0 R /Filter /FlateDecode >> stream x\KWOQsKNY`za @!)V!["YoV},/pvftY5{_ߦBM?O_rr^'gm-c>uVH_ev7p,1:=g6k2nv۟_,RV 7\VIi]lʹu@\tq뼚8,pҸ-L&vaؾT^L 6?^:-j7Jy6 f:>IʼnݖYYU8Y;$[>ɜ'ʉU\b~݀:I͉TfsNOd>6cF"uCv>$L_zǯߧۏ˿~ܾ{?;M:S*H l>÷vG Mڮ& ̹@Ţg,!D? %glt|Zy1MNF)S|y^%<7?O_7Z-3oO0Z>-cY]TPjo*oz{_NKQ'O ?R7WS%$;BAd%#j q# }'[^ a鑸D΂=|!9PZ%af.!qتWC׸]"w #o \l܂54LuaKC =Ez˳[ h.h'B ХO`)\-goZ >{#c酧poDe/ 3>st]RǐB fT'-W`,,m| X Ņ86S7[0`fK0"`i'XV܊7lGI@Br#"dvUOYp+YpQbJnЂ",1Y^ FJ>ݶFIL;a9EQW@R ד1LimIuO1Qdc>uD\n u 9딴y^Y B|)eS/a.L.<}ך֔ŨޫQR@ *C-h ir!Y9F*~ >j% &F![2GQs^ P/.@"V$iku0(̖"KH%4F}0T-Bl:ZɵX;5^-v`>92>t0X4⿖Zt֑I^ 9@'8%Qx > U%H$4uM^dž+V5`. ;`) dƞ+,DTֲy* 6$ΣOU*$ru XĹ*"^.T/uq5ju*iHZ u,]Hr)%iRbyBJz@B~,1-A$T*XRmuƎE^j#z"G@,!>2 }k gXvF@(HQS#ZL}kf,/L;Q[OǥLdOh_m-!av`^eT FY@j4l]Z#E#'F0|`T-;Op/"@~?/p,`?";젺hHwv|s1{; Zhz!,{5xQJhk`qc,~pỷYSm6n6wpiy`YΧgFY]sq!3ۈ|5ǡkZW<-mA5x?ٷhq$ڤa0@FV"ng:dQ R+r_8Je9vv+ {ck42z;6haK1V]T&DY}@mxS2ߨCE:-kTkՍ)zdFNd4uO hS xƱ^29ua(NXSCPG'|5$?Ӷ3<$V爊$&̧:^䆉Juv7a1,RT;z0 .g_hkx _y£6Y9ڹ#?  *F6' YrdsN<(ɷ&[Ovo捇l e[p?Gȝ2յu$ȵO1J`4wZg'D/htڋF#z›2Ca[[m3[ =.K₞=),|;XXWK@i&Qr6dNd@UES<9|lCR#iǦ:e}i7p;C]i @]Li?݁~^-fU4n . }lG7Ϙ?3cyEc=Ơ+?mܐhPV4:%Yh*5AZCÈ-,O u QD=]4/ݔO۵b!AaTE.7@%p)!Xk0yZ0r%,= PŲ o!մjj:V@ҙ!T\T1ͺ7eZp\[w>x7w|N_NUD4E  C_6L XU9/I0KhVzJ}Pf4 N/5h Ef{խb yJ~VK6Ҍ@OPFd^aE9Mx& 7a-yo3w%JRtM)l٣@ Q+2҆=b?z>:O[bhu A 3a5uk_0'^=4 c{EֈUސr7>T3ɶR5AH7?_#'iQוl~O7q䃬I53qcS+TViuQP< endstream endobj 23 0 obj 3582 endobj 21 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Pattern << /p24 24 0 R /p25 25 0 R >> /Font << /f-0-0 6 0 R /f-2-0 9 0 R /f-3-0 10 0 R /f-4-0 11 0 R >> >> endobj 26 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 612 792 ] /Contents 22 0 R /Group << /Type /Group /S /Transparency /CS /DeviceRGB >> /Resources 21 0 R >> endobj 27 0 obj << /Length 28 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 800 /Height 263 /ColorSpace /DeviceRGB /BitsPerComponent 8 >> stream x pU޲,Wax(QR< :ZGGdbp|AHP  BD3IB^$@ߙU=ݻ;Wkݽo}k^s$!B+@!BWk0qkq@~i/F4k6IDCU Z!Ao~[ozƛ0tv1YaE H{9ѫ=|dφ< XCViVs̿W"=7R~[NrXMQGC_0G{ *v"|]Cl\QA+UXO}yrE`Pf]mFvs|~W\f1wwhDdS+b˝B7\dΟYXqr@P}R](*9avMw{}{t~s@c 'X]b0Fa9q͏bְ]0[N{?FZ֣q~c#҈C6UtU˖ܿ=2:C>HX*!qX,b q9G69lK#i_Z(g1q-OL_>0ȗru3;ft6 HVp|0^^am<4j+0vigM&A,\1bW;DZz93S;ʙB7\[*fa]aI^ %'+hr캳__؅ #! qTȰfR- I )r9hlD͆a4?Yau1}3+UOLp*&(xP9۾*^H J+QVckKj+_,؞bެ2S$L87c3AN=$ P͢A%rCĺi#-b0;_1]bԖW 9Wn YB7\b7PCq2?q[ wȽYi>o^V1h 2P7Y,0#43O 6m3,5Ϗ[N"+y>+ -bW֘pNҒ*XR_PװfR5^HwQrh({z|h5n4jE~C)A +h@ʠI,te0[ )XaZ un6P{ Vg>#b,@DHgH3ee0ܮA=?Mqcc.u_}A;\1cxSZ}.lX>ĸC3~8L9f DVasf~6A⢯<RFGBy@OcPZ(a9.f7Ft3օh\Xb[Wp&0 [8:@VЁbhՍ?ZAl@W/W(:}N'xŭn^Oo]fي%]YO6yBn~ qL_KɈ? Cg# ƃg+B+h`h 1}T~+ FV,VrcH#BgqL_TF!☾#4B!1}8H#YW?^ $0!x+SiBLIP_B>Ww{Q8![ՕklW, W_ي Z옿}G^ҥIO^[b+ͯ=?ʫh.Y3qA>T:iS_u>ִV_9qag(t_˖.sNVLZ/~d3pb&}>|{ȖnnSv]n]XnZQ˖/u7b,bK >vJqWr\7ߚ !F؎վC;He 6 H%5G4نu{.M3kC|aIIO8'{U)m;r/^8+Z986׏*zYV11Yⅷgk+=:ᒀٻBmn߆.kgw{ݐکӾ:ժ+og[;^~>hlײ\ uOqrY!H)m[_*ڽ ~@OK͟6ʂV+Z9a}/uo Tl'ړ50pG=d҂t&jh %2-R/뻭J H5#cw:|h`9 XY wM9TfH ||ԔkLiDt՟CY숰OFu0Ёb9\r]X\G|*B0ض%! R;uXzsPhgϺ+D|cUmbmdC?$* ПTP\`<| j{M.>GBiS_Q_>6?u^v TaGm$$Ϝ%3 wuKCO$A~%ejOָnfMIڸINu9ɻ^W|>"B :vc;vʏdm9[6=]~ 7ߚ-ѢRZ_@]w>^W@*N6ޝQe%gj;Tm4ybwbo9͸ߞV% 7qz'd` $IT%ϼ0e@QH ܢ1W^u%rF\q6U_7xSI @Aņ-Eg̜?*Q_9qJdjP {+3пhh._7axɇZzs0jq,w*+^5Uj[/>3.EWJq6x5>Wœmo:8z̳|hUQ#)m2z}⫯k帠E(нWlGgB:F$={2~7![% pwa`qqu9Ĉ?!Y}wg9yb!H£Y+@}IP_B>:}uZ_$F!xf wdg-8^E1t=iމ@W,VrvNO7Wu5(S}E#BgU˖SnjxeŚϗo^vEWuiB6:9͸NJO$}a.ѨgFVTYv7I)V ײ\ _%N_b棭IrkeAՁbX[ܭm-frWD5ƊEdŽlRϘ5+5L䫵hגO2`r&hOo4J:f>_ZB߿-9[6c K~G[$wMȗQ70[uHm*WPSSz1iTފNȧRA!~?tX|"++\X\զ0~}1`iǸtJ_=4dHGQ. (|ҕLH> 1Cw9XhNT 3 TR0̳jJnf 6&w&N,aԍ&\?3ű{@& j{M.>u9B)Tjr7;V{>׽0 Q. YvhJWSJfP&\ >Go J"j7%iv&:eϗ+8PV0+7tsl?,+$Ƹ?|}D?l@d-V"C~[!`Ɩ+9{mv/^WP'(ԳB}s7k;dDW}v8h8q2Fv(+?q->4b^mp~U"xvN1ƛj ՎG|a3#GDq}|qK ݅>[.DS}aeKɜ?*W'-^̝n߆qFzS`) 0E"Q=--_E{/Y&ԓ cW3˕3`*ZPgBlDM8(WA``XaS#K0Pa*9ѡnYz%ߎq@k|  `JSJwp+Q#˯}3z}Zt"\|6S[8hBVK;ǽ8VwcW]=9|_4SG/ d|[Q̩nE0<Mtg@ӎf H⺱ʚi+C!9GB^YEDZL<`0hAX%FWJCuWI£\*Woߡ]ⅮW>P_Q_~Bq++烄BCxHP^W^ي ZBn~ "wBHx">% !x+S2M~?z!+S2M~?z!+wzyaSBW|HNb!H£\r |ntihi77!B}W?]}U\T׬c!_}u{׽gΟUώ(! ꫭJ5+/n]rGP_C_yvg+B+h`h ijb{3?}xc}nv嗤vgO&WPMvuL'_4+lIrQ#:P,Uey*ʫOM}u5ښ~}a)zh%Ůw:._tiRStǠmۥ1klߑhZPK=ʀeF=3}v*L4y2I,jha +l`xnh:C/oٲ`ڸsލmA^D֣ MmII=e7Y}k[3͸ذ~Eͅ >v]PŢ ]`3Ha8NcDpTL~;i7.,Xa-lRmdT7+ct((_ ymHÄӳwVywÊ0hεJ_u^J&홵!V^?Ofu3Zp4i$`vB~DUeJk s]PŢ0`D*r60AYB~lݢWXr~@&r27+e%V ߽6aߚþ#V@ $z11af*ٳпP*;K_}(Nht6vU ~D\90'm@Cۢϓ|O*}F3ayv3#~d[}*HճeK$2gbQ+b߰qo5v԰sN$_ 3{>"|;EN@B"7c-X/^p١>0,WPSSz1iTފNȧqt&oJKڏH[$T_Ĭ=yxs֯CT{|++\TQ+H!Je?{@TZ$j7ڒT"+HmV_Aj 7lD0]XZaJ-<#aE Fvb|>4Z*뼆\P}rgxy|x r2%*"bjʏ_OH}ƅAd䉮w:3tm+l@@u'٨te7B4^ac4ЏVVoG_K*yF\q61϶.`vY_W WF Gbz44U״jR\Tudž/޻w^#Nyްq5Wc-+y?P6__WA*uygϲa6c %pR"!wwT_Gn_-Z.]HY:>WFe Bza&JNi1TmwrbOXtn@lE_5E zg$"V@ "/8Cj{H%뼃5>Z}Zq }_q!꫸T* <<[ZACK$W."*:qE}}E!$msN/KwE QόS^GBt :x':}- d#[vLsλzLU۷a ݧWxU0-tYwhvsAܾ#oSztZN>cMf"0M:f2+c݅PV Z˖GGsؽ0q|CHz?t= VX`:}ņ*/gJ&홵!1F 7o3Gk1 z>q:, J_AVr At 0N|n]Ȩefw6 2`䉮86R^{oKU/Ê1 sn~e_rMO {\{3Ч2^4;w=ꙑ5U|>*:}j҂/C&@eEqBWu'w|evU zWW9[6u{6n5W:bٻU,j}>1/N1k ]E{侖~Ũ@Za@EH|D,,4+o}DpRm )6l|Kgk+C{WWT+SB_|ԔkLi\$У H$k?6#mn)Ud3X!p0u~ždC(݄HmV_ʈ=PR;!|.k.Y``TH0CH5,2☆H6n/A!^V4W.WPVY W-[Yr|ȹpI}&T:aYrwF4.G!t&Od^ED:wP_%H_e˗ʯ0(=A J$J%(@=`5+]O+3wC ;V_mظ~}b(KqzKIZyGR_ "ن Nן=UPw밾X]qB(+A=++|evUһ@}P}ПcržJDKQ~D*[|TuWJkv, 7G+Ze}3_O*O 6W.q}|qK   oJGq%rE_m0w6]uwhB+wvԣ./e3 ocD:cf0q,Ţgݧ+gpׄ1` d.p= VX`&,Ĉ^ btR"hn;ݚ3 0':4F̓ D(bPB ˏ$N_]:X4(uf:;V_aJMNiV>Z8-Fo[C"ʏ }U{ՓÇES;utҾS!? +,0!ޖw'"SCsCq#ܪռ=N`|gq/+k}_q!$APGy6պj*;1SWq?UD <<[ZACKS_yjnjôQ#}u⿥C&\D "A}A">p!c+ UWSĔ&9yABH0)5ycBBWA4WWWBOOw~{qwU(|<ij 40@b7;WW$`N_B 3b !B}E|YB!^@lB}E\'bB!A8 !C}EBHࡾ"1|87:?'ş?$&"~L_5ԝÙ'r6#s֯+Ƴ/#;`8QU-;keK ˭[ذ~EMm )f9U{L{Y௫\!DG5/w]D6O575F +St,:v`$Ò˜?%Xi7/g$H쬅UeUOe\8` +SՉJ-++ mr4 H7#1"d&D f^>% hMkר5bxƔWΝNWħڊrEMQt T~y##;u-Ueb)GO^f׌~z Oi󩯈O1W[7 -mlo:]OHq`&D LIA[k̳O;}ʤ">L_[Bc˄pܼ0+鐆&+`1~+j衭2_>zcY4yx8Y[^E7cz״ڡ023ҷWOuU|I+$P\ɜ?O.byuLp@P}R]Xo\Z#G# ծ]nﳯpo.F,` GlȃN3x?5w[Hi{-dy˜l";kaj8A KV}nF^cR*(ʒѲt]gy(ztR/6_P[DVLb)%aP=8x3Fv>m4L=fF WЇLe3+uN(gv ݧ$tLUº1";bM} qDpD ڴ*vH _u~sOD! c9]hP MgѧTz}n3c`Ry"+}{֤טT"|. t[t]-տZJB/&mUpF[;O 7cdS$M bw~HytHS"/l6,񴮀ho <#P&*!54|}ʺ *+fmJXW 8#-8*4rtI7[+LqO(WЍD\A(w'ͮ(G9u9zS*"7A%>ɷi$a[^cR9j)OGP7kG[vġ:ndh\-`Uƍ8j𨾂ov;?lDWh87g$pn__a!Mn'~3 Lӈ%TxZW@%(; !P-I7"*KUB݁7>OO}|ɬ3N=nӎWV!fC Jj>ŧ;0iA-f3 \1`Qڢ0,F,6ѣ1_KgF\yr|[ ^cR *7.X]EG[v]ZVjk]*>.>3s P[+s"ΧƩGwri{U6t 99AP"J׬-8-Qag֥|xN@7PuO9TSYveδy+t[άp\1p#quL XG"TIQA"[c ,ovFɩE|eQX Vܘ% Ӵ>Dxjn1xzYK%kM}yrm^V.תt]] F,25b8rڵSmX5zZq>5N=O. 6t Ep|gOAeAwa %2; c5ҔẲE՜ȕC-TG(z\|uآuƯEBa,ƧdFGBy@$5m!C|Ne4 XLyiRz酾oQXt+b~(NCt{1s[ ^cRZpurJ kUQc :܂# DҔYϰVq<7N=[D>:}qCYjStȯ`IR}z MܧǿtME@m=ClԖ~q?In7&P-xUVfoDँpUu?k2W|v)?t?/ڳw,"pB :} b3n,d޴)k+tCk"~6:ie' Ue"p]$DD|WVE @kkN3afHy"XҪ3茒E82  cVř1 ~ 3 $Ҙk-<{YK%k! lAkEтf#Y1l{OZXa@Cyh?(҈Χm=Gzu9qfyv1z>\mPR@E@Vx@x!ȉO $9a j!PV h'}JƔW:(40c >\WP'K8@ q+P~/a $9[8FMM(f/؇-H#AH! W"!BH8Iw !BIz[,!BHHj!BH8"B/f ;WV@_U.؞WS~Jx罷]7Ѓ~-HB!$ !BH|"B/|>x +h ω}h;.M?}f~k =;+r Mg\5zfd.._7iDMP4GP#JZ ί-&E?xo&4o|18[v)o̚KMO>0~i6OVV~t"bq8an7w)m9Qu<ч {ْ[7vimD?ʌxRY]a-Nħx ʛoob:Пn߆ koO=2X؂rlO_()Vg <4dm=ho5݅?e˗b@̕ϝ_ :`=rv02*~Bak5"}86qm@E!}lycFFtR6".R{2#~m%듛A}_FWm:#߽6;*b}e3|,VmIIO8^gQM9[,-]M,̩ ,սx![sE cv1MDBn#Li{Ҋ &{-" "PGa[ҾC;Ⱥ"_9^fY vXuW~E{2M D|`ƓX^BƇwP_:jxZ)g#t-5~h?BƛҴ+(`6]cCŷHUex߽/a;FxnrZ۰Da݅j]amj^k` B`K&9Y}*S['o6!c'v}3G ,+ݏ˟7ߚ e/[aWZ-k &w126W:jQ_AUK^:%ؐ#öRq t4f;"H2>Hc^6 #^:]6- WA_C"|оJFlRf]Y3$Pc4AҲDİ{_[`ò#F1 !ka=?Fb5jq/7u3}zq'oۑ#f kaCW]d<)h߾5w FF;yȭf0Kka3;yUR3H!82sxr} ,V@BZY3;''އB!$䓵WB! k}uB!f wdg- `XA }N$E4f!B,U˖SnjxeŚϗo^vEWB!fU~6쬅HTeUmiK W;d !BH81+yղPY> UWB!DWB\amҥIE%cD.TY]a3xrk}cꅱ.$9͈.u]A%۶KA;wʫ SD6Rv"Ikń+b}nߑ gFl)m ]wl0iJޣ -땷S#o ԍ;a3n3}y}u31SMF1C}EZN_}a옾f$st <4d2cwa)UE-ٻm= ,BA9093ȷtm0q|۔YV뎍/6 \ٳPLbДWPgLq ~ F }mybJyU5q+KN_Z 3}(A(V`́T>iDm>kW{+0Fޖ c(DgGÚ. p%BUBL9XOZҨ,cN^@LJĀTF2{րVzf|ho̚5v)/"ŮIR8piJo5[Y>:}A"{<%+GqE_!,K=GA/[TW St1mNS#[{JgEaYn' Sڸ86Stq1 F$cHf9[6Pa4fz W;V9 #u3g0nHE =k__$HM!4_Ǽ6rrt@CC2H.>6[s`#%0/髦PVY W-[Y9ȹpy}Be+"2aG+BN_i?ПCtlو,={2X|4]!QόDB\i ^n?u ͷfcpprę>wi% <7+ee.j}1ڨ/b)n44:t#Zf;D_8㹦׬aWZ>󅱿v1c-_3^2[?کu3V cqV* 5cLX2oظ6_7'f;$p|gOy_UuM/ݽA烐=<Xya~E}|xPo[u7b@s&zdO;&HZBSqB}FG@[:}EAGiGXʏ\|S#I"5W cvs7!w14At%+pEXU%2Po >2S7m" db8^AtQ_BħX#CbaVЎȗkK`ħ r ]]5>T+tAc>$6,ж] bRjb|?C>+3h cѣ͆_c@|nGSB+WX!HK> Do 'N5ӛӲ/gς+̚8j5,}loyh#$$Ǧ89 %d;Q!.ig;/$ 9ZJO&okLQ~QEt*_G"\wiܱi+!QL^;ű^<-h|awD%1v:yn(?oh[ ?h={}vI~; = _8jw{[vBտl{g_`WTӦZWSWUe3r !BQ_575aZƨ׈G8D&j?Ȃa $9[8F6dLye#!jҤ3'O~B!a Rj< q+g BۛNק?9 >qܭ_B!BYɝ++-x +h ω}؂1~B!? }E!f&B!D !BH|"B/ՂF8ϟ?$*b5Ny9Qʿ- U۲Z B!˭[ذ~EMm WOȩcC*TsMx5Wk>_zg'0PYXHhSM+(#~ 0IaeΟ?4% nѿr6 !8 $VvªjR6?L˸p8_$H_r`{-NdʭԊąhtHASBB '7]f,G1sgM W劚RmWZ*?sCIBk& \J!$ll׌~z Oi0)n 6_7'qQk&E)A!$` XcMrUf&tyA=UGREjNB!!Sn^H+- }uD)}}[YAm~ymɁ^_4y>X{!]=Ijy6Ao:P\ckmo?s p)-$v !n!60o\Z5GG>IW"A䠑v]XPmBfΟ~G"W}M!$T@dg-QBƔWM}j8_8J f"pႏ1g;!+~﯄BBž=X_uv^i:]qp|a=9hi $ с0-֮A/;/!01hBWF$Aߙ +r"B!!#B!D !BH|"B/Z}E!BB!$x endstream endobj 28 0 obj 22087 endobj 24 0 obj << /Length 29 0 R /PatternType 1 /BBox [0 0 800 263] /XStep 6719 /YStep 6719 /TilingType 1 /PaintType 1 /Matrix [ 0.247715 0 0 0.248657 77.56045 230.635619 ] /Resources << /XObject << /x27 27 0 R >> >> >> stream q 800 0 0 263 0 0 cm /x27 Do Q endstream endobj 29 0 obj 33 endobj 30 0 obj << /Length 31 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 254 /Height 189 /ColorSpace /DeviceRGB /BitsPerComponent 8 >> stream x pUǣCQ08 FAd@)(.28.8B@KPR*27T`E5 "[ aHX@Xͼ_ޜwv۷r:>ۯNw02 [J-I52HN0Qr"!'O=edaȉ:ida9y"^2kL*9~7Fx*Ä1D CN䟭:}aטTr"!Ye)T^&Laďk5&W~7 9/x;O="©c?Hȁr -98h-[]5m֤W{V,Mz /l>ݣyzɀk iG`D*QoMzႼm"*Z_~g)_jۜ0۶D&Ri)x?|ȞI;wlwwx"V6~7CZȎ{ai^2`#C(9o/iJf6w;䪒,"bϛ۹՝:w m()>qRrM*LNIGL:g(]6w:4G~R:rܽF_y g|:H7Wf.~J9i?jwDzЀNCg[= m7l@V NCI<"UάO2.m l"Ʒ0;z3(nMWK^Rod]var6953 \dv$ILʉ|#NSh mSӍZrVPi%-ڰv~ )rxEPi\ ョG;U4O/72`.;zMƥW~JNz7̎aR .#xNC{88 ͘S!%9LiуD*G$i\2ULr(*1-*]wXNr4`r԰#|wv+fPÈjr=xJ>Md'_ayT O88``A˜%%-8H-_3>% Zkt9 ͉ awߑLWu쀧*v\F&QD>,+*Xp1MBK0<Զ9oopW#s2ˉVuŒ ͏q0SgqQ#e<}W6?4xO,,+1kr آ 4}(.e<}W+ˉ|_nƤRw5/'}D1p/'}(kї?j )ˉ|_1J;YFFaʉ|72 _](|ԤƖ辉jC~Ea$!?d7GPGO Aʐoȏ=\1ng@ e3jz8q'oبAԋƎ]_bvk_VDr߽՛w~S{mlݴtyf ,9^ey7ȏ,G|K&}[-E #0-crg;?.WNȷkWKۜie7 xԗ'?kr$Wf.!{"H~f9&U:lȒtՠ8^w}qS -fUdEg#H~0ƅbR?q܎!ߐ2S|C~rʐoQ=܄Q!(e7JNSNW*]Qː]#W"C~;-vuc~0.dȏ#A~Ee784WːoFi [;.C!_I45huV",U<)W>(]OO?>|ڛ輻OՖ.Oב>2`q( jAwE|Rn))r6\!wztٶ}k|Kfd#M^Nﮫ_"{; 䁒biN;vƗi(E)A?|fw!?W0M,?Wx!ߣHSQ _ ~\椖Q_XmZCp_՗t~Χsf7oēE2M)(oַ(//n1䃽7m$4TO.}L_Q\tz]ձfw$U-Ąυ4:Ҕ~~ORzO}<|_H\|#Mm݄eLet" ]j`W-[mw$k2-i}eO?x!_E){%?<7_2&Ǣ(]ʞk|Zi't F6l ^j\H'? '?~e!݂xz!}ʘme7'| u&Pmގ!_ݱ7Ce|W |dMY7ގQRɬpS|䔓{Ì< ŚL,Q!?Nt |0ގmd| Zqiʐoȏ+Ҕ!;%TÆH͚ ʃ%w: uvu]A=:xw|gV4ug;>3c~>wru*6X3Ǭ؜g}g;-^D-Z^_Lr; |hS%eSB1x4˞E)'3y"U!/Nkf:sʁ ٫lIבcrE^Q_ ~0x8\~e"H~"Ma pn G> s6:llJ~"M>{>tr5f߻7|iJ~_E v{iN};( FC_K{ p{|g;򫽌45h[ryԗ')Tl*$򹦷lu\||g;HEщ4'w: ݺE'StlJ~0ƅbyJ9T܎!ߐ2S|C~rʐoQ=܄Q!(e7JNSN`X3T }_0Ɲ ":uDׁǓ&4"p2dȷ~ѩA-WcD}ID"%~yлRE*WK@{v.>Vuh_tS;+䷘w4b| UXw7~ʃD{vESzHu'"ּEDz>٣S}8b܍|S 1^ oۑ6_z{$nݾ2-rԋFnw(ʓAVg=49~MvMعޑw1We^$CiS0h6w˶M3Guh1$nݜImֆɝ3Og&rnٶ}+r@%ӻHSW,K9?uG䷝?|'8̈ 0Ou֮^_~q`F"VӻYΔ}W_^q?{i ߯߈8|'ʚc-ϙ9م?SÌQH#BDR%]oqY@N>MM@I:&X=Sј$.?sDbĵ zϛRP&+}S|ֳCÕv5O8ɈJ)%V@/w)/wrr~,sXL8B>!?$1\,9HtA~c)~~OM/}߻gWϒ֐Ka9HǑ.dᏫSXoWKcрԐ&Fȟ>m%XÞa \6]cI _`9HǑSL4嗍9ܻ(yJ)oldL(Ԧ?iq,oqu ]jC~Hbp0$GUB::1B!)] s ^ ^":R>W#9$?}ʘmPu#'oRy:zPta=G7,ԪeKLZJȷco|H'={jJs6~g78;iza0U }b/ .iVOO6 [}GEYš{tAN;N?EKW_ x;_R߾;o\ oȏj}" ]I /{ `xaؾc@nݲ/[_~u_tQa>5ak_k' D%߼뮏'M7~cu/ 5YL(4lw_aPTıHU3H j,Ǘ!~{-7V:sY+.سkvv,q$/(Jfhɗ ɗ#?{fONI`!4j$_>bo\,ǗKFc_mq&S&Q'VogoS+Yqގ015VzYY?9i"N>#7K\>{yԁ ~$KW `c_c<-c*洲pכݩkm]̀U7C:{fB114ǣ +w5[zy|+ڥ5?eyUۧ\t9].^6vk$C~<*T2ǣLӊ"{}Q/C~<ʐ (C~2ǣ 3#={K!?|ռ5sF/|oQ2/C~<ʐ (C~byn|oQ2/^U0 ÅiTtȯ{q!C~ u'?ƨ6l ~򠻾״=_M^g>c9ވHZ4 ^u&$Ǝ]q8I͹0R/3BgKg-ܓvu>2@v\x(_7"W-[l:/!,o ]ljgogђX~:uoDp-3C~|>{꺮`R *QtPgB62$ɷ.Ϥ.|Q/KqT{SksDb~!R8[u#:TVZ`rj'u\) e(ɧ0\e/l#|Kb>ކH%(~˅WNH292 YA*-_ªK] Y`֥o hu#Kb>ކHWđʣ()q}L`9l>HbYy)&tEH~ [Ŵ"|ʹz9E•TpsJ&Ri./;e!ob&'`lۈ̄*k|#+ vI>69PAz\?yV)i_qV[*^QtK{^+ ;u q., ) TiQ ;wFadfz- (k|l@_z!N~e I끵TAZB rF()fMv,!U8YXUEB%_5EN:ϐ y۶2Lzzz0(kr6JK1jZN;Ok ER8Dz- (>W'+NY>Z$m۷bfR;S 㽫7vL~uWZeK#[NrNnRI͙9$- &=-bRʧ?r|3--;9}7ߴYCq<ƽj۴6?^&Q#QURa͍H/h ߛ]Ì=Mz@onaQYWu+\K38j/NU|؆x1`F2bI+qeHQ42`fb#R@JZM{-[]ݸ>R%\q$ߨn<`Df[J9 fr{SzRFGG#V2oߓPɩ`Ǚd-ZDm"&EV̸BD#?˫=o'`د/C`/0:rcYb`WiLl4fSim̸¢>.&-+\gImszmj> >> >> stream q 254 0 0 189 0 0 cm /x30 Do Q endstream endobj 32 0 obj 33 endobj 34 0 obj << /Length 35 0 R /Filter /FlateDecode >> stream x[[6~ׯhnd Ч/@@~ʶ\*Is} 39n[.z>g5!4LڏZ×2OÇ~zxFgPiGk8)LapI_}:`H{ {0 }>/e^?!P8N1e<<D`,$BJ*P8D>?y F^$|E}x /7Z&m-\=X#v/)4cypED_n&|k]5-)(cIQO+Qʞ*@Ąa 3iF|i^͌fmp5 7&<-ՀIґVB|O[Ym9IYSTOw9+Qc=Q^Ǭ(ԝB8xdq 6TՄ;4S?ޚPvsKIcPXխ4 |jX"N*^Yب]O [ &de $ګ&ZӅ=;{g)ɑuBajoP%~8U R=I1͖ǻSU󁬵`˔Ch ]IY۬ɸ#HWNqŠ\d^UL!6b OSvuYu敍`xS`9)ORGX|j`kPͅ o CfjШWͪ=hL,a^+DG_vBxu=!TX!qE]7|g^j#ƚE{Oxl#'X ǁ2f~pъ h?_))p]>v8hX *(s*o34PڲM iH;sc* 9)}wUL$g@b+4uT] {$E9}P:z_Alɔr9vNaZͱ{TI;>n˞lua{Ɗ.uARcӽzyKӝӴ;]MTq|hP)W龚w4gxk@' R[_xډc'5ٔvBN^jzG,R)`A69X~o\DknF<d]z S'G;J\ endstream endobj 35 0 obj 2577 endobj 33 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Pattern << /p36 36 0 R >> /Font << /f-0-0 6 0 R /f-3-0 10 0 R /f-2-0 9 0 R /f-4-0 11 0 R >> >> endobj 37 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 612 792 ] /Contents 34 0 R /Group << /Type /Group /S /Transparency /CS /DeviceRGB >> /Resources 33 0 R >> endobj 38 0 obj << /Length 39 0 R /Filter /FlateDecode /Type /XObject /Subtype /Image /Width 900 /Height 557 /ColorSpace /DeviceRGB /BitsPerComponent 8 >> stream x Ž(%/*r|:$XȎX+у1D߃1\CPhcP#1h 7zf]YZ{ս|>k^3===ݟu[[p#cv5uڔ {O=6iƟrO.}t>}@̓wHzrHM%!ӱ{+u_/6b?!moՅ] эsЄ?^;P D1ylGwߵfoW,[zǭXD.nj[#:.9>h +}%EG@0we'1uɧLe#FO??9ݜRh:\(r'?縱OB@z)k>{1I,)|ڿK~Hn\4nHk($s$ڞW׮ybݛrnj}+c|k/RoۿO(>4ڙ׌WZy;IOs[niS\ 7βo}cF{uoQZp ZwWYe S.%ѷ6osp4uE6FښNr뮟i[sydU 4j{Ŧhɉ ^GMZYe CګA1{,+(޷qQh>(\|/pQ. E(_ \|/pQ. E(_ \|/pQ. E(_ \|/pQ. Ek;ԓ9'6uː[6)P!⑥-;\m)=p.Z2'V,?ɧ>}YμF^~Fzݭ>ycFv.:t^X5bp}_es ׇC=YgyO0@ CGyEy<^A}xk.휞gJ\PH =\tpIGNzoEU#Vwy7C[gKDl}[m SMq[1_7{ ,MP.a;.Z3q5.Wd.j!,{ ,MP.2n.jࢵ7Tptxd;Ĝ5on:*\4XT.i(`h„r \4.,4aB E&Kwp`VP.Bф (h.\4Xv< ,MP.FE .z(h.\4X&KP4aB .z 0\ %Pp\;(@E#pQE\4. P8p\;h0?L(`h„r \4.,4aB E&KwpQFE .z(h.\pwp`aP.Bф (h.\4XhÄr &L(@E#pQE\4. P8p\;(@E#pQ„0\ %Pp\;hЀ ,MP.FE .z(h.\pwpQFE aB E&Kwp`%X(0\E\4. P8p\;(@E#pQE Är &L(@E#pQB&KP4aB .z(h.\pwpQFE .z & ,MP.FEB,$Bz!$Z }!w(..:D.xT$HyFQi-,8̻KEk*Ҙ{g9rVfomNdM'E\4.,)KP4aB .z 0\ %Pp\;(@E#pQE\4. P8p\;h0?L(`h„r \4.,4aB E&KwpQFE .z(h.\pEvѮ=Sc>Y!=d„0\ %Ppшb9g[mӜB&KP4aB .QL5صs{~_g_}Vm$(hD1]t?ҸKJ5=}& ,MP.FEC 0\ %Ppшd4(ɢ \pw{8w@cE# V,y(4\4Mٓz>5L%X(0\(\޹(C&KP4aB .Qp|qɸ(4\4Mp=[@#E# u;֭˻MP(hD]4pQFEپ9ۦ섋 Är &L(@E#颶WXukW^mwݪ [7>fkפE .Qp}A= ww.m#FWNZrpȡSI 7JG\4.OGi)xTEڼQ5ʅK;o}>HP_{}^;K;#E .Qp-wS,bB!BHKxt_:?QpIQ(u/9%vN6=l2 tq`I4)KP4aB wѥ˓$XrQ74ҖowRC&KP4aB .Qp?]XzY,To%{]9}Q-3v}xd;Ĝ5onfT(@E#pQcӦ?LpQFݽ,[?f(hD]wkzlL. P8pшh`0?L(`h„r \4.ڱkMGݽ7yQ\D|POA_A]\4.ZZiמ\T4E49aPX|a6Em\t. }}ΩNԑ`ݥ&QK\(JDesD~P3z0Zvј: j6]2m (hD]3\Avv9M/3(4 uE# ۻKoET*iEd98MFd|a6( K4yeQFPvhDI/k+>&9hD] zf}o=v' )': wQC*iE'ᏋR(%P.FE׭}[ӷ}3SKK7mc{ A.6r \4.~}!mfJpQhiq)z_whsp\5-^ ]\4.reiS_EuˆiTS: uQo;:NqpQhe]4H+-hBm.Qp56f %ghNP vɴ1KwMzlL.,-݀0T vɴ1KwK.~}iE;w{hspш&zܾfӱk@\`㢣whspш)hw>5_EEam.QpصOAAlTZ^\. Cwhspш&g &\.d?d%PpшWnMߔQcpQ( 8. C`LC .Qp% }p @(4 uE#p8۷?Eam.Ƒ(( uE#p8( ( uE#p8(i m좼$ vɴ1Kh\tP)%P.FE;&E hc&9hDa]ԭ&I3SB#Eam.QX=943%(4\4N]=udԈ: upExv v#/T$u,tՆ: wp`i q^ʦJ+ZJ;;ZΆ:닞?P۾)%P.FE;>oV"{*\tMY]Զ vɴ1KwpQhu~F2%}Zm_}hgs9ctDbY6ѐ;}5sUOaj3aP]EB#h4R"hg6}E≻hBPs M;e.7s ȳwE .zFh-YY䇥ω ץFRvs ,t_, _ݵ/s_?爃$&J&Op'>sQKWߜ: wpQhMpѤ+\4=9IGNH!v)G|rBZF. eE#p"U~C$ws>D`˔ . a@ .z\3ث?.[DX=-*,cWpE3:|)|P̿9on1Z=߼'Ih E֒3 E/Fq쿼tU|bԞwPƫW{.Sj梾 miu . 軂̀w*qP&a^z^E"ku [)E}I]͙j.MpѠBa5Ɨ ( \4N%.k^^bzy']_-T6QڿwEYe7D3vQb]я.퇊>؟*K?aU3! >Ew*qQ/95z]ׄ\p{گXnsQ?;~wI~ΗEseü!Vj`]})%ڒDWPr b+kCP(mzTh.sѼ@~&*Ƕ(&{U^gڈqvz\sd.\Oλ{q6D3u8V%fubvlԑ86S=g4m\ZljSE#pQhڠ"E =\{3̒k%c~S|^gC}&O tEe_}lr`8.Z>]O[fVv9fZE]@'.zǹhCL_JTcLf3K@~NzX^]N5]6hbN?ɞM=e :1h^6̕ ݽ8>'OoͼfslZvVU¾溳#w>.63ۺ=E#pQ8M?wBCˆ:Z)9ݫϜfN鰩Fr^{6܋]341Xgv7IU~S@Ir 7ZчvUd7r~JҸ|k$Sa.j?R4*]e^ >׻Z`bݿ{W#j: ԡE#pQ8M_4?5kTŧW7juGM[)> n"EWu'[wddNU`|uL 9iZ*ҹh_Wc]%!_9tfr: ?.j7Rkum{ng:t58Y(?5ھV:˵IJ5!nCEJ05/$[+٥"srt2/7>/VͳwѹW5V/qfuKΒi{PޠfMpk83:V* [hf]՜ԴL.Z39+]nfhgG*52C/ gv?RE+BվH K_8nKJN)g5;v9?ѷN+xHt4W0S>gthfʒX.8fxf#uX E#~]%&JY_Fs0V$-#&6E~sw.LM}FMظ[}|[ܓ,5%~1!oG .˺`Q ]T}PM}dVϸPSF[ZV\#^a'oƲ(K.ramVJd^A_0|m,QXm=Lqgn2'3.vK~kLk!r7edž2.GUD%{Y˄պrg_ʬSu_QFYerVE#pQEU7)E.5|z7k rkEJ.B7@mm){\nF9y+-NG]3ӷNYl {dm}%Que(e嶏ͺL f;eʙ˷w3TiSuy?/{;fn]ctNV+tQȪu =TjˤS D pFYeˌ4ۇx#Sx*~`تBWPJ ݵ.^YyoF[5d꤬6vvS`f,.V7>:܀[گ<8ϓ+]6]y^F|yAK{en-jU.7O>BwE=>Mԙ@_Ӗ֘ Ի]鮓WGdo*Y-]z|lZRz5 j}Ny`jƳgFߐQcおܹAF2,V)/53Skdˍ9ݲSƧÒ&uҌqCЪ'Cd7[_Xuv}/7m(vUs 3qRJphGٗ'eS~lN>] ՉXTaVt50eTV2\I E:{*h"볚Y5MgԪ*=V|JPp[9>[zg+Oڋt/(m7se{`ynr-\ 3dZ&5jR'J尪5vɻL{.1n HAH-y.ǩ6:K[(5փmq6j]$TBJRlA[f*Ǭ"_4}})yh.sQ9K@j7LT`[Zk9&No"]s71sRf&JI|\IeWT2Z7sNl>kSY_MeY,R7FRhRst fͥdG3J}>cwYܤBtF֖*I&{jXuE:a3KduP7銻}fÿ}79TrwJԃ*S}v'[itWkݣ4;qlt+ZoQX:[IIAXM3:-^u3`ݮowF7>0wzp}qe*g|h%xVJ?~se{N|\z؄Y'ӣV[!>9'v[mr! QQ/]\}4WUm^n=.pjo1irM7RtZX)0É̜22'V.ҝ]gsxtFgNS$vts;X5X5СD&+u5NdB9VuNdNtacANIR:6n2-k"mB\ӗl\4NE]6.g]6`撹6'~1]1~9 '3={9ѝV;wsid: _v\%JZDc#.=6lG'umiF&gZfq^mƧ\]w8:>7׉Y8a+7/h[|(Qr{%*sKCNmD'Re奯`\v (9j{C*~Ju6f^ mJTצ{VPa4;i?p):<'֋͉=/DHcRTpR_azO夕NMIG͝{Dg]tfX-RfV-(Vt\6̊U9eoythrĬdfs |_UÉWEs@cvʖR;7UrRۍ.Ec.+ /$+w!8ôN!whx[]JWH`٣p{Ļ]nX.Ctާp*PAQKC,!e{Tb.j# d'(Da$Y|pc&$1rfN&.lc|%NUuRbez͜!BhwӍ%Ƶ\'}F /Jauv%C"]9}Jk#:{Yag6lم{" 옇/gw K5OlZCE>ֈ m=[Odel9(~vRYNƱq܂ѹ+at6z gL:3ܷ9HMGLmTVTT^*mvZ=k nDzwij͎5EX-I&& %>1)!:]&V+uϮ#WwK"W.A nSJ+:)em3w?jlߜO^E#pQ8ML5nmšGY%`*+O|eN$9nr.f]?`.4Ljg k3Rwsi$}u.m̅A|ckͥM٧ d-~zG(f8> j#X$)O/_ѩH|_xuT;qӿI3\`MA[ܘr}b6lݎCX,kr9;ـi1(IKBq ab j=2N7[.%h;۬ER!*Э+ɺ:A;5Bl7ne; f8.uɸjٗ]EiǻMEΨaIjj\v䪸6wkw76T샻O0N]D\U[`Hb4՚3֛C+H>:oI E'nfVn#Rhl [o4.:qaOXtt=cKGow`-Mƶ7mV9F4SN&شRr.jMN6ONtV%58{DٷN l.wuǛthf\s|Kuwu'8ֿ975ݒa73*ѕ/>rePrvqg[y? [`m{ˊxiOP.rAdOVw4SJ{&u$&Z;q-mwF-tQjeUd<<ņ [\DY߈ŒaWCۨ&uF 0cu l=ܴ<1IVDvD쮡?X+")ݮ&o ^%ь8x'Uu8{łfTUoU%ebMȪI2c_kIfy.U2(Uu2J(v vD L_S;5dluDA$hU=m-=6^Hݖӏ=q`O=9e}i{˶ 4Y6*Vj>v.jM@"z=w#KoE!YQuI&V]+,s{_QnA]IgG|Yݱw4Έi+bF9%d>0֕-6[0{y-3];;Ҋ mIeSY]J擹vef^6ltL+lױlo%^F{YZUkgƀK)2w$KU4ֵjfʯn0MIEw78ֈ 0mM*(5%SZriDmQT}4|o{ޑyp!6iټ~M&1FYXs=#pFazUm?oV=ߠĕf_jyV Mkr\Dƿ_XvUv~`dlBkXpxt5ec_~F^ rɴ>Uĭ#=2Jˬ?$gD1O,w[L[*Dh[-E#pF#N`%vQ4wv?lT6r&@фI˥@/iEϠ.:b>"v㢍Ǟѿ{oRUM %‚65 . E(_ \|/pQ. Ƭ\ٳOGu{' . d'tڧd P~ݷz:&wϕWnJ#'t}1G}{=f>. !@{LS]s*a?l.m#yu;ow}}6۴)8E ppQho.Kh 0W?/{>T ݑ_&?We_Ҡ~4~fiHMj m}ml$oEa];5vg߷i}pIɷ-}R.3&. E .l.p<3a=?]طsu;sOVjLʼх ٩}e.j5)5]ٖ[3ꄋ(9tFJ醈%(4\@% {(/L?#b~tFݨiЦnounMgA\qEEQڬð,V&i S.j'h2h05s{@ۃ@vgbh$FT&E"ԿGNNSJwj6L*fb.-K秏Q$am. Eg*^Tcu~̽d;&dhGPCtQE+c;꼴)*pQ(.]7}.58s]܌P{(ikvʟ\ݽH{' [ZvFN7(7ˎ=&?ywk:gaqM';6+ ~FKCq]}6蛰K[Cq̛1CI[OOu_Zvihkk3gOwtKg.tjZ@VњNR"j^6t~?4\q.^h!pQhipQhipQ. E(6}yw@Kk7mS.7-ͺ7ֿTbJG_|y5@? EU BB P9hf#^Wź5&ms"ЮpW5fQSMY`~ XWëFNUv7%(Ψsh?`)G׬Sw= 8Dm.AM>w_3O.W79lrL)wi޲{i=Ǎ07Q4}dG8-к ɉ#FׁV*_z{@3ɩ[.Q_i~qU&N[JKcW^aQy!Ek8+)Ku1qwcQ.U?O2Ošx3ҨW^)=foU׳չ8]qr;N2&%eK`[_^Er%ru}h'ONTB>Y4\:֝wI/P^}n 6~'O;EۅGs7mnQt9{@ɩ>GP6zTU^{. TmLO\),JgV~0_{wPTpp劆ʡG^#T`>[{ǝ{ЪgkS'zn>,$ݣYߗ?QV5S*fV i6}h髫ZŲڛPrQyRJ5 %)ᢕS_L{v W=8^]~iݛL-VbFzbnuԔnm?xdW|rm~[6{auBZk㸍_3uz+//Rbq bvr^2b}J`|Azx}]6}xku *~F5][mп)z!+Uvs2^];ƥ/';HorD;-LXaBh]1[yUL."niU]8bjE+VKA]x! W=@ې蕪ݰqJg~תkS/OsX hAG\2oz傅aMxv3z{6BrÍqBWٴOUUt_\;U-Gcu&oxQƌCiSý.!4?+sѪ?wґt{{˭-\ep`d+zvJպ:0zK;ӷ9]\C(zpNuO8n]vSMQRv]5[QS&[?NTdT u'UmUL.(Dsg]}&̩$f]?;Bhr֥S۝U][sƺU-噧z\Fz=weUh3WE:U. &P*f~r/+ʩ*f<5+pQ讷&zvm6fvNo\˹;N:յkj/WzїLwfnloG>\m(h/v=/pQn>ߵJ]_”]T!xN9#>9"g K3E/~tQ+2#!+wQwμF!y);% dھ|v-yjcrQyN>#um?h_[E1'zH5>6_ݙw*[mu!94EAMC}i 7ΊoS[KdXzu=m~_Ԥ1)п]3uǐjtkE+6ݽ.Kd]x T׬}ou2Smxj@ҨDo7QrQcDi*Rk]T.ɯĮ+wQ%Fz$ ͬ{u!Lםm ]W^ˇ;;CɍѮ%3O.}T.*;*'mћ#qʉ\<'uGXTkm`muᢺuQKM_XBԇ8$-ڿj(ʠ1sQe%փWU6 I{$ z5Ik~f"\;RN*yx#''q9ٝ%9_).j^Eo ˯N_stݫ N-:Z«Z# prEy+8G|v\Tɮ?1w]hrQ○v=kQwzr56muuYi^nx=b): /=d̏**oLk.Tٿjc yMnjP_gk=JJM U/3hխEuנ\ st%Sv+kVɥoqVѼ428f>3JU6oToE ]gn{-w }]87Lx9jc(n,yJM}ׄ$y⩊?m޲~7@PO_~%üZn،#J/䗅Fm.*%yZ8梃].J.=bӦ}]Q3K~%gC NR9>C>?U^<\T{čLm`C@hܚNO9dݨ*gߤ.F}>pbG9>Pa )1TIT嶑Thkg^p9ɩJNƗLXvrTb3tZB9`˜:Ƿ)O]fByл<>8SBybC΢RegWEU!]㢕]-F\vTc+ W1şV>̇hג>sn{ V!!hWלC@hZmճȎ\ƽ͵LqԔ7}TCtGÕ2u%Qen;.seǟp'5u7{3' I?C IX_\fCߣoS.r9W '5,ʮbk:Sb2jYᄅVRu+wtx]²ꚺsqV^5G V{|v-YEr]1춵mh{o%Wy=11Vݍ:*L*5;}[shPXǃZ+dvOW|v\T dT%Z7X!?U^+-ʧW.n?`\bkέw:pQhQpQ{JZ"zůzr飢gЖТPu5\ZѪֵ΁c(EEi'"jƹ((8pQhQIȺEMlxB\Zv*f|fCo^;(($Td\Z6" EEA:hwq-BE@4뮟Yiڼ1D^O<_,:|a:}瑥[ʧWL6e;)ko2 Uuo(ϝtл}9gmظ>U8 η*A˥Y>=(GMtO8)?`|R#U%#x~]4'-dR$|sʁ_yW:LX.i}\^P}]w\:أ'9)U9en W\sf˥Y>g\C"Kfcpw3-z%=T r&e;μmel ;jbwqȡk}gœ>Pf< vÅFHpaLu:J_ܶۨXld*̺K_,5x~ۆilG2'rF{RDnu6w$KKݿ҈k-:;͘{(P2Ǽ;/溤+w)9\'Z@hJZv=ʵ Fmthַ]J+\]X_g\bS0%PW@h"^ %4 RNT ԆԮ׻#ܾT"T vcF+S{=DU5{u횟-ZY#w}!6ʕKݳ.eZ{Xp*kJ7tXYTW,[?WA+2 tØg DkhMpK;'a3ӄh9,N9Pf<=pao)KoLePnlwl8?J\ۼe>t^CuQK8hFY~Clb\=2yԐ ?jd;Gw(673A ʑ<~"CqQ LhUSQ7Wa\-LP J͑>1ұ(3%8}].s0qcYcFW1Yͤϙ7A*u.|;\a3zn]];ELUC&9)UN1ؚHK&t'SK2KV'sѮg H\.A4EgA^& v5cRnwޅaMͳoR&Y ӷeec/YR$,}CtH<ߝc=Zo|ugnTdWͩ9uwvNPF]zl2U؜*?U,fɧՉ3i`):fCfdˢ`[|A@ L Wu.~h3OC+4DkhvQ.b{,cF[vV.۷l}SOV#'{|Bn޲)gfw*҆2|=*BOvw>|7No9۽ʳ颲AE^J\ʧWU.W!٢U1]3u.@H$~,}XꈲܦڬF#}pm׾9đI^XĊnegU%w#ƺ vwqXvsQV.[^jTإVX2ſX*RI㷎rW^Uֵ=(Gݡ_+iCzƝDu\4HHdM.*{=9>ߜKqɉ$]*_iD7'lݭ6"qrGtC3Ի;Vu]dH?8ݘHRpyƐ%nZ9|=w@u պyG"rW^Uֵg\C"K]Tn~ ׊wp?w҉c!7ӷ )I]XnPpc5\ʔ!k} / >?0EeAvM+-3--[ߎGRE6v3V$]Ij縱.\rkJg"s5 . >k>+I%/ Ώc9uRTEsQ4\I늯].2]D aѾU9|aOsP|rv=khEꢉy8-_Vm怋BYЃ^{Ek{b?wz 7J?OIW?[]]:Cˡ{7yB.?CFC\d@\4mbS* *zB' 7~M8cqk{هh1r]/BUUO@4E%Gv׬풟/ :<\>fUzB.:b8b]vZl'?縱[S.*Y lջ@ T5_u=(Gvg\Őp*&\tZlK~Hz*/W}{d|F-o~ Zud}Qv&bxnճ/[.}xkk^aַm'VՒW^rA^l rշG֙Y[hEpQ\ƶX<9.*ve-Z>zE[¿mź0=&~.bЊ]v͆丨><nj:k]p 7NiSOsX UMBdk]#Fם)lOFg/휮1cF+yjcޝ j~R$+E5]T{#>9zLֿ)٢0^ۨoJ@dpY7յwݪ KA_XNTk`_];&ǖ/6/b ZF[7:ts9gy ]H,@>P6}nj㻳SՎ6i\n` B{~ȥU,yrIh-9|Cz \Zvx֩O2Y\qyλI"mЄoqֶ2 N9d+=_Uh׳V.z}sUx>((h;#r2U2yhS$fwҿr]f+xiHfU< HC]s %\ Y'3y3NK))_ﮟ>JhEU+7.=Y . - .ڄ{dgyçNr=w>uҏYGwCuyF_=rhE碷: zpQhQpѦef`HMen^_~%9߰C]ࢭK\tґ~k **8j#j*)'P-*/ț**.4fhfO[SpKthTM=7ZTZ.ŕ\.$[TхjQTCY8PTBmP[hyQ)LoZۭib`iTT5U}NOSEџnm Z'&͜8AyBu,޸+w]}ѢΩofO[Ss=4yQm^_g*G/O])ܥ޹i̝d|_^)EE'is&tTשyJ]RBmBw*ieSȶKdy7{rRP1u~s#!zyenOa윬{v |7N2fjj?ᆵ0^HT]d)1i檼FI[d /{=ƥBoX>q*-ijfO[N *.bEѢh"椠b"ZG_/tDӢЖʩSW./S ?M=6>ZVԴ9GVu܄5OMȜ9uwsV40=Ѣ-˩J|2<4**.A-bovNV_)i\,Ը&ogG{#ZTY :@MQGf޻{ϮԴ9\Y۩XViFq3W:.ݾYעZE-_]R~K641x׆@VԢҁ&k;ӪBjܜjQБu-:Wo7- MѢMe˯1+a%xh> owCPzKi]n oqs3ks:EeW_}9Z){lC- MѢM_m5)-zUh)9:<0ABSESw?1+! ָ!ގ_:WW'E]}mb0U .(4Er~uۖ^_ϓ᡾~_uME7kE㤠b"Fj෵E%% mj.O:exUJS j0ŕW_+O,Z01ifY8vy݌ti]v.kW؞I?_TL]hQѢ4r1y-k֮ +HJF>ztۛ"X^I9pp?#G+e>{}=/3_ĭDZq{.nQdZ&x!#3]fcN-NR7KLJU|塮y]dbWm=AߜGMq=?-:uhpRP1u~s#!zEN *.bEѢ(夠b"ZG_/tDӢЖ1SW./Sg^ll|-ぷ55OMȜ9- MѢ:Y25VT W7;'H0nLj<|mMΎ@GVh@ܢE)Z46S'?oXw!6G֠޳kᓘq-Ȋ [(4EfH%fd¹2@^K|pP}vU~7ݼѢ(ErG/v3f%*e/_/v{MBO/\hQ~ZڢEm#7}_jطkW (ZEc{ǟ|횊l --hlnJhD]p!cPS-쀎hP;!-j{eƿ77cK-1+ay7keDLz%-(SӢW:.AGi~U.E'I ˋ폇uy:չ2dxHrhas YF"?69o夠b"FjML,fT{6fl|TF?tv5#3]ܽw'D`dy?IAE- -*k?8p1y jcdhTeƬyS]EŅƖ,EIAE- -j|DܝeTژ~z< SۯHwihqRP1uSBlW-*&_P#ݘ4tkA?kj -TL]|7oHϴlv\[BlW-*>#aTlCS]dQ[ԹH5Ud]hBP_BQ^EFVN *0m[~*7O.1+!1i檼oXu/ȓ45;ʑ\TL(4EP1uE)8- MTLhQhb@BSSpZ":ТA*(4EP1uE)8- MTLhѨx_{Xb`o>zTT\87=Mmⅻ슷` G ǪKoy*64m[$ŎUy+sWOҢ!:آx6o y/9Y}_aA lGNBP1uE%դͺ*-*.T{-tdX74Ƀ.*s{f/~}N^OvN֌Y 2.9Eő\)*sKM|f-V*ӢʱQۧE->*qբ@*(4EP1uE)8- MTLhQhbY޽w'~Hi寿> >> >> stream q 900 0 0 557 0 0 cm /x38 Do Q endstream endobj 40 0 obj 33 endobj 41 0 obj << /Length 42 0 R /Filter /FlateDecode /Length1 26008 >> stream xyx׹0~sf/#i4$˒țld[1eb 0B̚4$, mMӄވ@iҴ %͓_InӴ mvZK,d}~`,G @"(h)3DZH߹iEMB \lХ'UZ6i--yy][4ߋ߹z___@hzZ^dr4^lXybh^_0_D.TTnY] X__'FBJ1dU@kBFTDlb\$"?qjz,~hbm͢KdD`zg u%t Cr}Ƀl_߳sG#ȭ#+wt\rW7Y:ù$K^EaT*V7ű.[X瀭}fL`':PHntp޺u|̮wφ9C8\.OLʲ9Q@ibgO\cPq5k ~Ea7\eɰ'E[(-+!Gwݙt`mFi#K+2&hNM剭gmI͍^zl\Q<^Df-_F{PzCI'*zz\`vva_"oQE"P`HՑKUű'PSTNT>,Ѩ. 9IgSźXAď5w 6BO$! 0ܭkJR>Y:TWZҔR㜤舩s[xIhKVG勞8_p̙/Sd@9N cJr0E ]ff,GE(t4z9lE51 QDF <j]|]#AB0Dg:C0}9^Ĺ@)H0ch` ۭ5k֮QY#Rm[ 0 Q_!R!Ga ڋo'Mͭz`Ԕss5kB2\*i{tڋ{ fnr$f{eJĠ b `q>J"lp f ,:d` JJ,}-Ϸ\jl iQ胔顸k܊; SU**`3l9~p$WdЇ (IvkԆ$+Ӱ"_kkЀ, RKd}qD&[ u :J )afP)Mpp&<>2aЪlщiO@/RHpv]^i* | ~w~~24}FQ33ཿ_|+V\ЪU%9耲Уbb6u]v_X4aiQ\UThWp0BZb6u+=^|{֋lz1`בvQ2mqҝ`.}Mr ا-?D)h dKC6 $H+Om %`.?xV&;KVv +a\VL_7UWVfF]&BӶ6 ."EQ0{i7"Ɖzb֬R|.&Fof>\(WsmN'=0 WswNh8ભ*sg0:ν ݥ^W.WRJHő* 6`e:X|^Xui Ң](0'4A9(.  5Q,:wa}߸~ڽd΁TC,iҎH-K'tMk4.Ӵ6tO˒i/>thU7F⠈"";r?)Ea vVZK^':Ҋ NjB*a=('@(D-jzUT챉YpF<+^ Z _IMREgev: * 6:9 EEhLr5~4HQ$v0 `ߵ5QM I&@f=xOnv~L&'W ~rp+F70>E*MP9<~hcŌEQ@BgIM5vcbP@cyY*y*N9"VH#TYX_\6koǏUvm&Th7iN !򲲍K…$d4IzPh=1eW+eoᲐ+q9+JBR̗K&:Qw4%f|x`6HW$%Vq쇊TΡ YJ  wUT4u\]Ju6pE RɅLIR/`} 5RQ ΤΥ. )8It)^N.SN)|&SRxk 6`a S)\_w.E^=kS0t R (ҖIԋc#Թ%)Φ@j3ZѯO^H\P/?l~ٟFmԤ)g Ӆ>^|5ٗlSdY]H״دm>IL)Rz:r6B/JmMMt~)"Y6x.{I6Vp(?4nFͤ0=#%]ҿ.Bra^{g} ژO tj85ʦx{ (5S""7ˤIn̵ :/p MdB'<ֲ5?^zC?KL+Xys\QF堚TTEhkwsN%?.*w2G-,ezSn#}nǣ4'PfʋMβx`r^kBPJ}iMB_R«JyMJI2Հډ-N:8 /R$`tMt#0J#DmXqQM-+MJiXMJ1%]螐F&12zL`ZUh*vVL߼k̕kC$F"t\eOJ&M?X-XY$:qwj^X}i~ݣۼՋ呢ۧSe*;} oܹ&S`3JSSЗyh9tń21wcf9eQ'JS*M7H6M&<2[]j"T/޴g\1M)c *BGQأ1ԃ4RuQR.Ie%GI$8L_ylՕ9u0Ǯz^g6P淭rMd:f'`P?#rhU!TGϗ}_:r>#.SJ" "V^EHT=8q%TaǖmѹdIny77~5~gs7xpCٱc3+cL_'~h%)Fcr6+%};LL݁|2OLtD ϟ0OPPF Fh405 ]`IF(:h$&3S8#@o ҊS=/IpN GDJ T 58J+@7abL-]1o\f30d JZx]2H(&x@&1f;ZQ[#DB[o܎2pqpqqAr$=x7}(2J9C`F0B'P2zq,"| f*{*ō^3`dis|ڬK\!=TfN@ib1JpEuYtv2qQu^.kF[yzl$5!U甜Nr~sNX|yqPAujڡjq:'@ Dgsȹyyy٩Ex 3v'[_\՞T]--A; 0H*oЄשFe Lo-=wՕ =,IMu^ş)weMh\U(9B'`/c)q&8N.R!O * PHnVa*C3{UWCZt3w_ l._tTLHS}Hm2iT/75S/1ii&޼ RI=J)8X\0YygbFw8PWe755K3}piQh1z qsYWm2Y{^ٮ^7}=gL@ཏ{,vNqtr;PɁ(bj߄ >Ɋ9#\}=5X== <'v:-vxP;qTy&4V@2'Bes0se_߱lZxIqgmc ~XzC=eȾ6]o}m#[q?YQ-fP҃űKJŮ.p/wc@SX` =lQEV" Պa(\:;n@䥂X y:3Q$gIf9KvO1}i}&wx? As{~o3=3rm=T:aex6"nGzWMYA"`O:,։u %tpk+u+_vІ69Hdԩ t}noԻBW3 NA3o0ӥSglMMmAT24L4vD A a"\cݛwBw~;#wzW}C/.YCY'wsR:7ri;o{[kkFU^>kkf\i^U^޵yJ]3^̄8t+"kxh[I#<<4/X͊52G_i4cTgU_^SA4uZ* <\SJ +dřsm G*Xk⤼UDKM#&/;pjHW`>OJ3el`-U4:4Bz>#:>\**O7)*r OjH\'y -GϦXP׆(Qk{kW>\ղi .0APrZTƊLx[`l*-]&"F'(ހe_E;(L*w'*~^F!a#!./̨#2pL?֊GVk~r֝ϱSUU{&[X\{*z8_Soݧ6jHs$)%q1jVJaZU\1b4 YG9GY_''JsG6OӒ}-ȝ9ZIzi!GSXIͭ 77tʆ /xw9:-]wM3^͕EͭSJJ =XByMSPgഎU,d8ĞKȟL} 4!á{q2Pu@IJJjD J$[(H#d@I 2ftHu9\@XEByI&2x I&f>@"e[2zY ɰI)~y[[dc2~GL.2<*2$WdȺ.5;e_ }cr- бI26{2~]~iLa ,XWG_&zyeeHs_NV i*:MWh_ 2|0rzd)Odp2>)Zӭ2̕keR/}(t&QLJd-֮M?f3[}Qڻ衰h\fX02 ڀtjge#^9+y<[ƆƢXR2[֨ >6ڽ{g{{xy .t zdp #^zA^j_\{[d gxvL vg0 5|/Zw5mXϷc^noBll!*:߷WKl=\WbeGK9ϖAf:]؆9%!ښ N8ưwċӁӖp#JtHբ@}X!g80.ɓEKV,|K(77Oښo"nyt[u}rm1I_eysKxG(;y۰@uuuĩs<IG5(o<`he'.e5"y2a* A\7'F|J|@[1[A +pC&Îp"ǎ?Bz<AE !uwou'^y|$7/ |{R4}}};a >$}]C~r}1BMCz_PHžP(¡ +XQBH$D:M,-x~iɶm[2!5lo$j V-d%?\j! Mng[η&:3G.FFgxP`S3wt+PXEkn.F%E]7+4BMo51B+zpKb+#_-:v /{;kk*/rK͟_-"46r|Y£?D *o;0uX?om_IrՏ@yjqS70v9x (剡t?%6;#XFKp1)wD+Nli_oxLLO_VÛ+.,;/Wе{PRT:M^3<.,f&GPȮՉ ~Ԫ~/(VKhW 8{"3Zܗਫ਼qgWR^ݾfk Ï?&ϟ/?f_c_D Èlaņb0mNa.̅5 bRBe\"gC R0 ͆Oχyw "bþ "G"ȟw`!D^X4lHLYpq^?6qrcXWs%sKRޛ#S_h6zi"A^*퓸 d z!9*qHmE g%XVTA D%I`F:AuVbCf@iSabIhOPNeyxP;X ft$y5܎uka yǨnjy<Z/Ƚ:돿{y5r׀Wm؆~ o)7R, ը*t`hmŢO#Pi Eȹȅ7B}*t?t)gD,k0eOZ/kYˁ''">5ڝEu!p#ъbL W"Dw {s#x_b~R`VF4[?JN9##ltBboB*DFTȹ1͞pEVkqla7kw6ٌp%2䯣R  A iW4]hV¸+ly51O=azugqmPZ>֖ջ͆77S:spuu8/1[De J+cw8QEdoSy*{ATIyppӋbb $) %!UuU$iJNOb2FVILY?7m МN$`Mt(fI{]X'olrGoM%`[@$Ny6o$K$ě;?*oa XVx"\$y4 9 ZȑUI8_cIF^Is׾6ٸ6Y{|lB/'$NfFhMΦ*ߢOv'[ }]`ad<[WkK*VK6'Ğ &qaYϳ훷@sҟc+ mMpzCI\9 ()$q*7gp& $nZJ*IG:9&O''Zӊx4M65C TY ~c şW$.ҍ䕓Xc7X%c5z3X]˛2n%q~MHIu]iH"1 N;>}1#O*66mHўstgfi+o\ \EǞ|{:g\]Me&?ګT`` &vvC3"i0-fXVNGgۗ1E fYyxPd6}2!6`/Ư $B>…g^d-UHFxlt籼<627"*#yVExH϶l4`2p0&7[-l6C}z]zXcbL̳, 4'QĂ1-c1`Ã/ZZlVv-fBbD` `:v' Fl{g@cjE-˙ Gn|:Ed1'0BIn˕ƍW9ru~<-lBo8ŋ#".Ha0D򮝋 (_'32%t{Jy9J[G{cb 筗X5LЎʭՌuҺ+pV*[I3gA[j/XgAkikzĚFY+g ɸik IuV8ܫM-'ЗH!n?ڸٸHװkE\ ^3n듚+zK"%b{yEz^-hap"D@u$?|OQ/b޿͏;#Eڍ .{@4aR B԰L a_~xO/[?Mt4m6@kZ ! gC{ \YL YIZx!p9`jb}Z~h<{4e4B@ `&E,`q}ĖY"gv%MLnԚG<+:[3oĄ1JFk:d7Sְt?Fѿ|07ӭ4C1Ý9O7:(@6ݝ[- Kyv ̽3I@%w>8_`Q#ӝ{`wPLijpPCr::\xzW;T>6eXi3<1}8;]MǀɱX{kbO]]UcJ,B(3*A{ip))--͂`J]nb2oZ\SnBdb܃WTP^ͣ5ϭt᱓X ibY۞_\x~[oPqӵɩ_7kU,2I|(D^g/A).?[NЗցNDxxuú`F?h$ qk؅|,Qӎ|Bo4?ۇi̾^=shB0>?kbylE#g;ۊG#uwAV<@Dž("}7IɃAr.uf Bn8>f&: X /cJʩ@4b 킇\s\0QW҅]z ֆkef'mZuw1q{<`f[L1 pzh3ŜR rq}1C&:TT,)azX;yֵJ I(#x덋jΕ1" X, (qvX`npXPn GDE@_ߤ=?| cw%F?߮qOإ'0d;6 &kk= >YZ؇?(-+E1ADQ{!q#Iw;(gZާlJ ˵S#jTҘi5z\Z{"&qh5zj;;u露5%R aJmS3"ZxX!@űX>昛^, Lsخy"Z"},Q!+1J+KI}xi09='ON4*fMNLdx#(ťVH[NoW7>}~lHfv5kS6&DhO Ķ)G>}Ws:zO≤sS_vha#-­m[SS^ՐZ.w|j:k?=I,>kB;Sfr ),WJe w*tw)K4&d 5#TIRv!_HRYpOpengPdI\&F9zkވKO6_4@BLu2#Ne)H>jjxA,@]ɤH&6ږ^Zi {GWwoЎ" &+xqf}ާ)-Oyz^7C1zo; ry/zd4l%֪˝Nx3^^R !AłopD+ɣ?8@QO*LX<ʛ՟Ԣo87Gޥd%zH3w۟e*Q:0^ _ɗ=#U|꽎xi,$O"̮_,t*8jMigI4(758МĤsEnNWԯꝉ{XmqYywbyo_ H͕׽bEa ƮKp/U~"!mz+L׽P#/{".l6v\ilÚŪ&,@ IsMaEd (Y-nSeʔ]`.6mz*[}T!њU~ h`qZнcJ`8 pJBAf=88[űP:HU^"Y[u@"~#%6Ɍq?bɼvwOqu!vݴƵuϘ܌}_ς O÷QlfH$L*9hVn(}I^o)/VB +x(ssu:>g-ּ/+؅t[, {,VvVM0#ݠwv6.Ke"n)zTՋi#04K Qqڵqwt^b˧P.vͤτ}}'o[[xKՂt&۟8}0˯yxp}t7_R4xX$&D)O<x%F˿ĿsüA?' 㚖,cʿԂ>uyas'b랭<`I%[S&g Xji<'Xݽހj_"%A e,š)T,1~wy_)N5Yځ&3?sjk\T$XPLg|1Fe%\BD Tqa>O7i)#aA01#H{tI[7Sy0 0vF8fQ~"~)9?DIۣn8 [gcX)}_^Wƣo1еGTcaysgϺefjWgGJkeJƆIښҨnMF^sTFAUL([rUfbZL6D:on ef[*ϴT-k-AMASBPHEKӏG-Z+2V iPGъP2l:2tfӴȴe*tdI3Me#ǡ<ZwL>^%юCގv8jziת4mȬnZV ZɦvWȋZY"Co͒Ŵ.ұk׎#g+PDW,[iبs+!GHh]N7,.Kvݵ3ܕٵ%ub5Aw{iǾ۟|daVȬɅvκfq3b1-Z#FرpMU#t;螆l_TɎChc 8jN׸糚k3zzweH݋#K(<""dmÑ]NG)Pk>2K^7vº㢟 5E0lHGo":@*GӛUiBY\85qcqvpV];O6z.nYqZeze͡]X9/! 4 WzgŴ^8,0һl!4CxZOoHDl8.a"0䲆!ԋd!m(ЂP'MDڦ>jpj۔P/xk:lEcY{4(i:>e8Txa8´:Tx1a`W( a"E Ceekcۣra3=/UM6n lgfա]H]lHa@Dg>=+ #)SyqEa]yS֔<࿏ˉxxqaB@%ieZR!E+ŬLeHsiƠ 4rZ_" 0^h8_&aZ}){LwGhؕY`ItG?B$Ew':Xgɚ"ڲH+oer+SȠ}u}Z~;YϏEnm'&$>1Wi:F˚@oiW;I;I)K=KݿEQI;/ut:Mf1{bF>[(L@t;h t51MQxf[ @YC1JyH2`=p: MC 'wюM-.YԽ?K˾& =ƍxfvh3K?h`ijw-GgKa˖K-DLZ։CkufG-0Y FȊ mU(̝tCP<hÉbx B -=7| ̿k|_s=xߑ>;$l8d}e> Lؘitep}&)f6&el  m@qg:A6czv $ڎsbTm}9ZQ*\ /FQx!$fӳ,$'Nਗt$iѩ`O|]'pJ*!jH d 4E  gN, ^WzLra7pЃY vaW /|GNf睼?(JDJ$;{JlK7QA ܂ytD1.)!j>E762p=JЈ0z;8)`4'M5*jq BIwN drԧB4eh$2z95"Y[" endstream endobj 42 0 obj 18770 endobj 43 0 obj << /Length 44 0 R /Filter /FlateDecode >> stream x]Ɋ@@>NU-i &'IJ5}~bṫquڝqx/]tmm?Ȁ"j*~U du? )~n*~4?zQJ`5 Gjgc)'ge~ v+q8Fvv~\[t˲=+AK%1. endstream endobj 44 0 obj 520 endobj 45 0 obj << /Type /FontDescriptor /FontName /LiberationSerifRegular /Flags 4 /FontBBox [ -176 -303 1006 981 ] /ItalicAngle 0 /Ascent 891 /Descent -216 /CapHeight 981 /StemV 80 /StemH 80 /FontFile2 41 0 R >> endobj 46 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /LiberationSerifRegular /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 45 0 R /W [0 [ 777 722 500 443 500 250 556 500 500 333 443 722 277 777 443 277 722 500 277 610 500 500 500 500 333 500 500 277 500 500 500 500 389 610 500 408 666 556 610 666 500 333 722 333 722 333 443 722 500 889 250 333 250 500 722 666 722 556 500 500 500 443 277 500 576 333 500 722 943 180 722 ]] >> endobj 6 0 obj << /Type /Font /Subtype /Type0 /BaseFont /LiberationSerifRegular /Encoding /Identity-H /DescendantFonts [ 46 0 R] /ToUnicode 43 0 R >> endobj 47 0 obj << /Length 48 0 R /Filter /FlateDecode /Length1 12644 >> stream xz{|=wfvwfI66dwI&I$K! y,IH F,&PQ hPB[j/ gm}hmmK[JkSۊlnxhۿ~Cf={{g!@g-=ahd݈{t^׋)Bh[~MYXvڥO,_ҳSDŽ l9"lŸ^p[o| MowQσR(#o[$o' !Bp7Jk&d pձ1p(&9c w^( >Q<,=Db~x>j]|?O J0tT*,k JNEMdy%֕nI/J'O$n HDق`[%8x .[ϛ li"+ >U$޼OzNz_:-[$K@T$uK\D:%=IҀğ$eKG%ӔQ JǑ[J$j {HnK%(-Q=lv1fEȝ3ǁ7BJy  @'{VmYo21ldΑ:WM. Mp-+6*A)́PJ/LKOVѿIp}^ٴ,Zx箵v>tW:Omev幑Bɍ,Jdk lzv^-g{6Hιl@lh=ĄtSdg ΑUh \Zjw gcrؕLP2IϔCv+ԘY)3FI%Yw˕HΪ`@fږЉ *\J٩`ږ%eGʀʝ+f-Uts"f}*c6'lֲ%6µ ll>X&&l;dY&Ze%PQq?BGBL~gZ}j( HRF;%>e(OIq4gjUcIͮdĘa]e* t#%0>c҉lԜ3cwmrU\$s& g9wBM̫MK'U]SI Ofz^^aMK$ol/l̞QQ^1'U48mBیs%wEN;ML_dIZ}&4, o$_79Lܣ\ EB||,6=!t<Q?BN4 גP WN'ekn3\#èL1p'OΰQ 9-ؖKLK\:du=e]Xj5}~a2I%zЕ ɒ-ę9Ah亸^܂pB#jb11#F vq=!Щq\_!++ N :N-]'um~=rԪq&+7y9}ۍ 3O1U6ITx0=4n\NHf TU^._vsSs$9鐞n!r+|ual6׭~qCeҁ.]V6 *oN0)>˭u~9{t=R:uʹ1z?7+#IZMZ OΌwI qRWV bwJ%棃#Ux|-*`1R^m3FkCKkq&@~,pG೫ DO{[/k-ϤYwٖ\8L+Hm'ۜ7qs卭;qG[nX!4DžxQ/!D l&0$ҽbDE-YqT<.w Ъ vHݱjU sI^'; @:w.r1_ v ǵ?O y^"` z5Q ȧٲ,VZhR)/S3Rb` #}^>,'erFTB,c^Ƞ҈N$웆 {F^Ew>9szWDZ\ 59jiSxjyC4]ĝkD\)vI7_iH\#'r#KV9yư耞ׇĹ"(BTݢ fiN?8`bXUy[})~"'҈Vq/r!]oVd\b6ށDX~Z*M6E1‚Dscl|)_ @ffQ/ @9< T`]Z u>mFj0OI 5g_U$}K5xJo2rXg|1!K_01wHw3ҭ]4Zːy!o5xS G4N nn6Ğh5"E 95B:5h4)4'4 ~( Mc88w^>oheo3%2Yna5Ԛn!̈cB鿨EXTXuBL̪c֋ &eF٩eJ))BP{BP`a( Z@:{%KC*+ʳ ݠPm *QuJ87XܦV@.cRy>R=>9(XW4)erWN)9Ue * (F=7خeBf0ڋlHgE`cP)@2Q($VPGsХQdEW g\W&JB+C2+n݊hlvppv-yArs2ݐ"]KƱh$z,&+IIrJ?{2kUVWgxF1O& *CLw:{.~70>tS7%(n䟮QdvcaxЛc33cۙ_T^蚔b577{FybBEY9'>1OJN/ a ')PAWt8+" !5Yy҅>HB<.Naxy^: qŤSHtM~[ACj>RTþxB#VwVA"lUpc7qg\ahVx73< қn5 ;sCDog.ltWqoםHfcipHɐ7d㲀{+H;_Wrnӌ8:d6],e[?2%Gr>,˾|gτ艛Wiyyzz|V2gY0⻻etr\+[kb9YPp]LOv t/epShS 䇔PMʘ,<1d4JgPw.ra"ϜՑ00\.;&t%EtYNI ;%,~Bb?z/Cck(A'&zG3_ԙ& }[i+fHyA{^fRZþtSbWHi][* .z ?R2fpIxsle阕AEoX36mpk7cSP됡۸!A7Pxgm/G!$98L&` O w`R!ypQ=I%?%:J~ȯ! CXc*r:Q'l/`-x^bm]t ANPKN }\:6|#o̳6č[Pu<0C v5<W$#"w/)Ɇ4DGhNȳ1WNvyc4M"PSM|˰N"hn,ZGvc-620r{W}0\Co ,D{-O>sy}S0h=(+<!Ղº `,Vp\)!ƧW[< x "Zl[#z-V( `n\Ep$W}y=Xu+Zڍ.n^ ղ=uݵ1/_sa=u` C8*n}҈۵wRO;p;|K:p81bִ7O k`/bl"$ aW=s_n`VOQnIm09ԏq31ԻSX@{XRǚ@1]1:|=͍r\kz tPMċcSf$Vnd_|ap 0*Q.CGX?J}k=҃im f]%.,̫wR*v'(mpm| ˁhW -y!%3 ?Ii9y wFѸC9tw}랁g)%6{Fm@Fx%b!X.EeU xe_ok1Y;K@ 0 hZðy#sl䴰> stream x]Mk0E]e{j4B݃1ﰅ 83yė*w7V8Z$:6JAL_~ٖST"eu8 q q8~;{ ~ ~uo}9Pa&;4Xo> endobj 52 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /LiberationSansBold /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 51 0 R /W [0 [ 750 666 610 610 277 556 556 610 333 556 610 277 333 610 556 610 556 556 889 277 333 722 777 556 389 277 610 610 333 610 237 333 333 277 ]] >> endobj 8 0 obj << /Type /Font /Subtype /Type0 /BaseFont /LiberationSansBold /Encoding /Identity-H /DescendantFonts [ 52 0 R] /ToUnicode 49 0 R >> endobj 53 0 obj << /Length 54 0 R /Filter /FlateDecode /Length1 9608 >> stream xx{xSǵzKmY-[ֶ-B_l$021`H6vip<@N8INJBҜ/qR{6 4|=m(I{ۓ]eH{Yk֬Zkl"n>4F~i3!rwܴkRX~|J6m>mn-س>}4#?]sKW3 iN">{#H߉t7 ?Dj1ͯu ]H c;7wu8*B)uM$r^wq!vB$ɱr8sNNΒ%\ْKhəٙg[)+ /%%JId\PJ0s&\m}pD/B;s#++n^Fڅ <ɥ;UǻFZU&>-B]d*) x;)wnW>t+*6~hUU#ُq2yZ޴.""+8SQAQYyT\OH muCTRcoyؖ@dɉ%t-fsUQ^/Vi#Jp*+SjbprKH4?B"heAXgEjZ< ѯcp}] YŶ򚥾u5*m Z66 Y55յAي]l-;mE.k"aA@ȱV5Ĥ;\9f9[ّFK\bZB߮Be{nnIDA3Vrl|I` [7uCR?U(3]r淿s-[Qz˻V=޿hcB%+se{s sMMwfN93ܢʅqRE3179]\ pvn'&NM󮫑%mH?]E*@TNz4SGxAƄ)A#9MYqA"ЊID""LD$e7Yw{lnGJY)& K_]E$;PՏg՘R%GfK<\\=,?8nX+m֌0}--.h|kzrܹ~󥻖gYšM\ۿ-:+r*Voz%J](ցUT@*0eѲe'^)E2C@t&< WQ_t@+߼s;_\W{ 5WnǢ =Omz_ ܦOK~o`]y0/KqSMM;V1aM)#,V`̜ qq@0 hHg[m6Zl5ц蛗s1 ŗZ6ly<1[<.1]$,¡9IH,ޔ^! pPb4*NH.K^AJIHHkYI')D%Ej$ 8))MIǤdK GQiA xYMΆn^U@cbgh'{mo|aᢥo WeףF0_LABy^wg^1jLj_.B㚑 $9b~pIUUč\mjEhc?a&g?ֆ+1ҵvl6=` {(l``6$cAަj6w l&o}|pZ}|W4Hrq퟿ kZ`:fNM%Ĺu 1v" `|Пt} ?\ŋ2x^tW kȒy/8x>TǙ6Oi=b稖`ߨEۨ?L&ϫ9ZWnx .$# A]uC3X/\Ѳ==塛k?NOG<|-0f۫Pfo|8[♩zΧUX%Z֕KzJ{i]q+jĕjչI5ձA9<]/*xosA':DjcծX}.rԑ+ː/Y/2(L +o(z{W\gex^̭erL_q~ ý2|S2be/ oʀbwGdʔ6yLXQ3Ko_6uZQ/QߐQoqgezD~L~^at(Bee42o.bFn9\e}Ce3ayRZ0 [uE/~@2ˀ2uh6{ Z 5sԡfdxT>-2$YfαBJ'ecsj)roˬMc򔜒5 2ϬKfXP]l+]rD ; Y|yuLH;x5ꪡx.Ozpz:u*OM VVY jJ,TwꩤQΩ=F`7P A9hqmYkv0[#1rVc=k&+%}䵚ʣ}kb,#{Ib$%L&yxOd \Ek3evmPCu]VC,Wb_qE"f: Q.#ק}?IҼO4[n{6NLd"2mBxMFNr%F9jq Zҧ?bmp]יG YG:Y|ĀƷApKFɨ`Utp@Y_4Ϳjt/4xm2 sU9W«ۿϧ߷kʪ==z.XCG'w{\8q-ؙ᳐hcaOXYI"R!Z+6 a%6\p6zx.#qk=ls Zy>9,Q/Ǡ>:hC.#dN!z4Ȱ۾Ecr[+ŐTo7o8y|r;C+z~ +au0+TmXn,S^&?'"Cr<37?+݆3|aNA+gp&!wco.=bZA^ErMrj@3٫uW8LC.ϔޞΎ5VX475.S.Y,/,Kټ͚e6 zV*[֤*N4b<f8ɔeRBRnTPr$rUxa1Y.ZD!f(5=X?, Kj}ZD>Z\#B BKutK4ĦMp99i2cՌT8vJZ-NRbbæ8eh8XzSVYm"Mʔ)WU [~d3<٘ Y =)nNs-C؜ W)U.6BLk[qڮ )ċ .G񍜡9NLX᝞nЙ٩'-D$zיٟwZ,[lkG[*wZ!s{eU3A c F!HM>EH7Eef%Lͷ\q7:{Si؂JMmD{ƶBSֿ}tNPUeBJ[`;.ӼJXy]r9Bj%9ką py*l}WOJiƊ24G-'+"c([YݾTDKƫɦղG2-eoJMsRf62lLHO.FmfŽ&)o=YqRJ/npس"SSl[S77LSZFqgԠɥ AnydXcKeڸX7iB˦99FߠT̩)>MHwz}'\NY{&NH(̗GNˇΞŪ4Foocc6hj c0k<)¾5'"¾SxihJ6 `[ϋ!ʥ˘4u aP/*L7 < 7?ϣdxck1" wd:l8KqI:K$njLFo` _z p@iE+gZw x397wz x5TqBt Yg d%o+"K֓0J5@ >{Wrk;F~P'mtj #|UGJ]@r5Mamޢ?e=Tv|qz8?(uY/hF8A*C_q'2ʀ1uC y#}@}p'}ќn-BA65pQ3tyn}ovnUL ^[gkm b-ѼluQ[7&QݘvGl6j |B3pdWg(vF?g>>RR'+5})ݾ[s޻$EmΞT-5UtމɐD(4" L-HMNLPhbB4r'BX$LLLv2~Ȼf]]hjk9S endstream endobj 54 0 obj 6995 endobj 55 0 obj << /Length 56 0 R /Filter /FlateDecode >> stream x]Ok0~9nF] "CPhCt~Ƽ ?7Z?z\)~lx~2߬dxu$RR\ZŮٖZsT8qYF'5wQfQt6x܌JITUw{ik;1žX+vte0ZŴm]ʺ^~ۅ'I;((]@'K@gh'#tAH$SzaO28E N|zw |9&!\/Cgď8r~27k6j03߿?k endstream endobj 56 0 obj 326 endobj 57 0 obj << /Type /FontDescriptor /FontName /LiberationSerifBold /Flags 4 /FontBBox [ -182 -303 1084 1007 ] /ItalicAngle 0 /Ascent 891 /Descent -216 /CapHeight 1007 /StemV 80 /StemH 80 /FontFile2 53 0 R >> endobj 58 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /LiberationSerifBold /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 57 0 R /W [0 [ 777 500 389 443 277 277 556 443 333 556 556 500 500 722 333 500 250 833 556 666 777 500 500 500 ]] >> endobj 9 0 obj << /Type /Font /Subtype /Type0 /BaseFont /LiberationSerifBold /Encoding /Identity-H /DescendantFonts [ 58 0 R] /ToUnicode 55 0 R >> endobj 59 0 obj << /Length 60 0 R /Filter /FlateDecode /Length1 13132 >> stream xz{xEoU]==d&3MnLB !7[p3rb ((Y²@D>5"b Ad%ExAu0){&,zvqdz{B(!D#H9̓24I|ᤋu-y͹XT>0w=Dz7!-sY8#͆'}H~!JaBJ|;RDN|P}T+D[rLL 'ʞl%ۓ+$q/Eb_:w!D:[UI2W|2/V9 {5YD$b l܅$_ ]3b\/)6Ҥ=DHGsԞl(bH]b2‰tMT>Pa0ʥD~X 2%*NM4*5'أ: "f Q6u>w4E~Fd:Ցj'rS^o0liArNT %jGᑎl]ǯࣼQ mШˊ4Elo&@$(%UgrE6KmA;'sג?`w .vTP2B^Ÿ'P/uGBwI<唩=?gAT<ɞQKfy6\}~]ݓM.zvJ7g:.kPNUPD(Xar40I° I:wS]inQ2X[le[h9fCiE\ /yjT8%,d~aǐ7ٰO?J3S9^2Yk>tkݴ<mZP!9 =Ev9Q QN^Ϭ7.ȇV-_fUMc܌AOiN˷߶

`|ܾݶVNLPτևd9XvDuhO2'H3H2BS(@_PX[P'h+#:' #Ħ&iz0٠V<*=Ɣ\HGOS&au / k b13/uH nS.8uThpMGc368Q}&씶 &@GSF/OJE8R/7@53E dX_9x oBC\|Ƕlc4S\cJ(=(39Cy-w˘`tu<W&*˃= '6-UWDz;e=j{CY9[j %cyW/EI*PL'),i<3ğqjUr>*WG7c#L2)wnO@NcBFHru<6YJTzhά#wS?6IWicS_}w᫙FQ -٨HGɊͦÊ&DuδԜy׮وI$&sݖc?dǒtm1ts oK+..ٹ׺\1m4RlDn~ P^fa aC(<#C2t7J̿^򪦦1[Oyޘɉ!Q&>Qliݐ)4Wٲ'}tO9Zo9 |C$%[ ՖfG 쐞96'>ڋ?^իp[Ϟm-}WhMD]yb4絡9qV[j vyycCuk f) im(ꮾ/߯ZOl> e'MN5zآs8*i(guq+$=Y"H~kRuE׷=OPS=#ē:Tl{:8<YEف!݅!Ww\to.(IJ w"XN8n-"=2$,#,jv'zUS47[hfo"UrTۥj_kfNSȘ8ZΔ!]CiMa&\]@ = ةtOWѺ W-ͺӂ=_䘆qKL~u}߁_kT{;ƍjݲt悂*s5qògw%Եy!XKU!9] jS3F9,D\uhש7-^o3w8K}Tc S7 ,Đ֛:no{1z.ґB۟qD$$ɊRqcl GQ=bG춌>HC'a$6frˑrx9-\].%y+RrRV*JsMtSS[W_PMS C_F3=eUs/:K {.yf{CNs/c[<,Mޯh.(tcUd̷ef7>MOfMQ[UbMÜX*Yw8+"+_eQ2ܮY#t"+i\Zv4AS)&֤[iKZEx6 .M)#O>B1g2ڵ Q2<ϙmsGQ_Uu]kΪuƚIr'f9.wMxݑθy m[y-bB~jȰP=jۭ[F{n8V|7vР)lKł9ghe'fset̶zv?x )HN(~f{3z ۵Il6WhK2X$-ǴemʞG/M5}Gvd=?ʊ;aVޱِ%-`[VeGDubTPX i3uVl=ڝ-snzk3}; soRHJ:Ubl$ɮXZtC-mP)=aĜ䜔@X`n44Z zu&n􄾴 V BpKYsg{^߽|iiI,ywM{s /٣)=;6l m~ڼLgܔq-do"3&s4j.! 2jv[޿]hLbBU81vA B3v^o@ZvR&h7[H"1IgFբIFb"%)&BvEgƊXKӢ~xz`|RL bw!- tt'*Č#gĪ(٤XAu>r1 D%D#lz yS:Tzz !0\0[]UmJTK|:lESҌ烟6Ϡcf;HC5ymt7ke^c!`m^oޝn8aLZ4aH¡ōg5҇MA˜)1}3͢}pzz7dgv`]3Zg#u2v5;@(ˊ97 }⡤#ҚoٲV{Q&n\88q ;.χxs.9-dW􌀉߄y;z~T;5D ;o#KvKUpFxHS3L=i!T{ ~4'D,)h~q^\yvk<=x }s GJ'7оDzyeTE 紸BkS)L < gsvtcbRBNKl.:nԚs!Sp֙矬o\Јs}|hfE'WZd%Innx> yJ3HTK2툨vץ28ci6/$:M` y&;[&h+͙f Eg}} [wWfسgU[?>b%R~ߥu9VУvow,3I׮'%}e[F$ c!ur7QO!criR;,=n}|u;QOVjS+WoxJ_N~yV!č*-\dQ%;Zuk6O޻l{MN~g  󛅋+K=D< OUc:D:.VK񒴈EQ>};iq_8ˡit9Yj @2 ֍:VN_j\@q(>_qW{_sŏA\ zMx__m袓ncMjXQyP]\89ĥDzAwO0dl R6ϖl5۔eH-bEP$"H)RLEZy Cǰ142NcӴid+ʶVi+*oU[M[]dv.i%RvL] =A ?(TMT0MƧӔi4> $iCQa . z;k֮Z+Wvvرzɶ?.ޣ9'EsB\V,o2ڰ$o&q$ Ļj 1z]H="ћqGG qXٙd;Fg ӏXKPȏʇ|WRIJ.H6M|ʬYĹYWgmfGɮWynl{ {ћ֎Gb4Dzaf&kw;掴dEX#"VkD-*XMpGT(g(Ngg?%l,vq9V=bc]uXb"Ҭi%ǤMXj!B^z%wu637ιc01I yj4:2t!})Q2~QeYgKkS}Q2גy[ 9U}]CP. Fu _Ñ=>%_aֽs]FVki2)j&+VzH )_ PSe/כxi -y—l>c+3q{ (-4ؕǭAYd?ERb{m.BߧW%ϴbKDbocq1, т5pG\N$S%sboM\ѣ'~sC}Hqۀ>)d^m {|>Sփ14"@D1R)6QF$GZXmcGP 8|{+Ud+`D(۷>ֱ'b -;eΝ6O  8,b:gDij^w $ :ZWn6/&t$ݓ2~ѬRp~)(w=gyRYƣuc+nn7zxW$gȤ}Â8) uJ`gǷ0N\}Q dj!)o!O[#x4}=/cPwj`+V6mX';D߯}L|qp8nz縊>B*H3 *ƸzUR{: z0:Gy/ }B}qe..tƼLl96*BrXne)wԶyqф8hKJ 22h-o+gdTqRYo kd Qױ>.)dV&h*6@#>6+ut78wE6rgkfg\0i@reDTHR I`L>N0}B!مUt;`ǕN ^IW%uUQWrdZk,JA"HwEWG'NwA3GbY)˲ft覗͞&54G}h^75 s,'{W)yE"XFǚw ?aI%K̺J$_zZ@][p'OL!J Nd(S]$Թj#=$SsxnV¡}NC?t;N;-F\#N7L,ZC{eyN;ɳ&Ub%jE_ 1]LHF0 8C려H 7HnܰWJuƹؐ>xWA=X3#rA;b(EQW1#z%d5N4T r$H鎕O%y$i* Io|җ#RDd DNsqg[f`Ч = ;H+Ti/jwn bY_p|@x5.$s8ښ;% 4!۩`Kpιd3G>)ƻ'*l \)[Nbi'Er^N"=iA[,c?+hf. <[Y&9 d[&]xnEKIv*ȅ.e2&CpRG"AO')ފFI%5r9]$M7>KtأtH.3''#e[ hm15l#'}=vi(/K(BB>,kC҈,jq|C2Vd Ҝ ZILVҽYZ'ݒllҤw&%guE7ɦ$&cwG,Jjqcl'|ӏ糺[DC2}0Wzo!Yws~KNY>Oex~c"]!PzF|}M꯰vfGeH5ĺ)t_Ũ lkV GÖl{NӋߍs #5obk _6RxeWbӯw`;mC aք7 ӿ 0:ݛBFliB:,(xl Q}ſH G$(^] ~lք ϫX~ƾhB̓1! bYj6lvI{XoQHG>|[h|ݍOWlt3qL<Ґcxq,nBqwIB^%#)ST|.Il6װbf|?Ftߤ^*>1KJڳq O]+xeţݸH R@uĞ>Abς1y࣏g2R1Ukxגq;̸CK!(຀kyRj5QW&pRҞƯL+~= C65!.{˃NT $1 t0b.7*-8R@pA_/bkE \gg]3g]i-|"f >Z V3>x_x_p ;?ޭU>~Bqǖ;𖀣#x# xS!ZY7ҁ>~` K}jf U><X{\{OV]^^t@y+<Ο ;YKa{mа57 `qlMyMݡ篫ڧZOm=6>F?/뱿~?<F@XܮsZ3T2BU ;Z*[{18X.r!,RLoR/<&` xT#m7wn:Rm>)6|6d>> stream x]M0@:n--B0%~д?rqu-Ԑ4ys?eƘuۉW۲8mq}mo%<?}|{ؿK4.nui69fbjs7+ڡ{o4ӋlD IT9d,BA P^k"&׸Ҹ.Qи ق,Y> endobj 64 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /DejaVuSansMono /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 63 0 R /W [0 [ 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 ]] >> endobj 10 0 obj << /Type /Font /Subtype /Type0 /BaseFont /DejaVuSansMono /Encoding /Identity-H /DescendantFonts [ 64 0 R] /ToUnicode 61 0 R >> endobj 65 0 obj << /Length 66 0 R /Filter /FlateDecode /Length1 3324 >> stream xV{p?;{7߾ȃdcٌN؀q뷺ø>k`ã(h5pQN!֭ۜnm*.ɖ1eCaraZ<2d! x4fWK<$&E ByгLvVa %ևwv*AZ(|ᢴBDc\$}0һhJQ%&3XXc2mA?;I*2ta)JQJ_lJsMtZW#歮J>Y,zu|6?.+tE/u~U)**J&Ҝ>?U|2oe^I~7zj0d^wKT1}" jCM)ɎeyJzȂCLG{_IR9ߵ١)l*|h cƷdx&VOq^wS>|݁>|MWU|EߪOFY'T|9V% 33|b_Hc>|x^l"b/ŞGţGdq4xE!;dC ?H:`[0DV~8]fߎy>2cW?eg}|ﺐ;{7=Vܣ힅:.;;s;xǓ^?Ivؙ;qnzGa|܈mhSAX"aȂĉG¸Պ[ yKlM&nLf:ԜT|DMf^&36)|]n#9Po 26z+B֍V5Q;m|MW xG1N|HU*VU*U Ÿ+U *V>{DyåsyĈ(]'..DY,Η b~"ixO bm"s.Fn Z2T:  ML@Уt] @WU5(XxS8<ER&|LP1ƫ8IF!ۅÁh#;ZVeZyGh=fr`N@M*I0*3@6ꃨZ΁Z+jOhB(!'g<ɩd@V"gY5:O.~ endstream endobj 66 0 obj 2459 endobj 67 0 obj << /Length 68 0 R /Filter /FlateDecode >> stream x]Mo CE>ڜHU{aZ@dH B#Y~m^[שּ߃Fƪ[Dq2(#薳'-jGJ.1lp*7 nC~VpF`] uj"8OJySU|n".Sx!1;!k:EЪ&F-E2UE]vDQUdssKKՙL|tߧzZkFʽ/} endstream endobj 68 0 obj 252 endobj 69 0 obj << /Type /FontDescriptor /FontName /DejaVuSerif /Flags 4 /FontBBox [ -769 -346 1679 1242 ] /ItalicAngle 0 /Ascent 928 /Descent -235 /CapHeight 1242 /StemV 80 /StemH 80 /FontFile2 65 0 R >> endobj 70 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /DejaVuSerif /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 69 0 R /W [0 [ 600 636 317 640 591 636 636 ]] >> endobj 11 0 obj << /Type /Font /Subtype /Type0 /BaseFont /DejaVuSerif /Encoding /Identity-H /DescendantFonts [ 70 0 R] /ToUnicode 67 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 12 0 R 26 0 R 37 0 R ] /Count 3 >> endobj 71 0 obj << /Creator (cairo 1.8.6 (http://cairographics.org)) /Producer (cairo 1.8.6 (http://cairographics.org)) >> endobj 72 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 73 0000000000 65535 f 0000137502 00000 n 0000004308 00000 n 0000000015 00000 n 0000004285 00000 n 0000009049 00000 n 0000104116 00000 n 0000012453 00000 n 0000114406 00000 n 0000122676 00000 n 0000133774 00000 n 0000137347 00000 n 0000004533 00000 n 0000004734 00000 n 0000006600 00000 n 0000006624 00000 n 0000009025 00000 n 0000009342 00000 n 0000009364 00000 n 0000012429 00000 n 0000012749 00000 n 0000016455 00000 n 0000012771 00000 n 0000016431 00000 n 0000039174 00000 n 0000048005 00000 n 0000016666 00000 n 0000016869 00000 n 0000039149 00000 n 0000039469 00000 n 0000039491 00000 n 0000047981 00000 n 0000048301 00000 n 0000051002 00000 n 0000048323 00000 n 0000050978 00000 n 0000083527 00000 n 0000051201 00000 n 0000051404 00000 n 0000083502 00000 n 0000083822 00000 n 0000083844 00000 n 0000102710 00000 n 0000102735 00000 n 0000103333 00000 n 0000103356 00000 n 0000103600 00000 n 0000104281 00000 n 0000113310 00000 n 0000113334 00000 n 0000113777 00000 n 0000113800 00000 n 0000114042 00000 n 0000114567 00000 n 0000121657 00000 n 0000121681 00000 n 0000122085 00000 n 0000122108 00000 n 0000122351 00000 n 0000122838 00000 n 0000132427 00000 n 0000132451 00000 n 0000133026 00000 n 0000133049 00000 n 0000133286 00000 n 0000133932 00000 n 0000136486 00000 n 0000136510 00000 n 0000136840 00000 n 0000136863 00000 n 0000137098 00000 n 0000137582 00000 n 0000137708 00000 n trailer << /Size 73 /Root 72 0 R /Info 71 0 R >> startxref 137761 %%EOF tuna-0.19/docs/tuna.8000066400000000000000000000253741437350234000144160ustar00rootroot00000000000000.TH TUNA "8" "February 2010" "tuna" "System Administration Utilities" .SH NAME tuna \- program for tuning running processes .SH SYNOPSIS .B tuna [\fIOPTIONS\fR] .SH DESCRIPTION This manual page explains the \fBtuna\fR program. The program can be used to change the attributes of application and kernel threads. \fBtuna\fR can operate on IRQs by name or number, and tasks or threads by process ID or command-line. CPUs and sets of CPUs can be specified by CPU or socket number. IRQ names and process command-lines can include wildcards. .PP \fBtuna\fP can change scheduling policy, scheduler priority and processor affinity for processes and process threads. \fBtuna\fR can also change the processor affinity for interrupts. When \fBtuna\fR is invoked without any options it starts up in its graphical interface mode. This manual page explains only the command\-line options for \fBtuna\fR .SH "GLOBAL OPTIONS" .TP \fB\-h\fR, \fB\-\-help\fR Print a list of options. \fBtuna\fR will exit after this action, ignoring the remainder of the command-line. .TP \fB\-v\fR, \fB\-\-version\fR Show version .TP \fB\-L\fR, \fB\-\-logging\fR=\fILOG-LEVEL\fR Log application details to file for given LOG-LEVEL .TP \fB\-D\fR, \fB\-\-debug\fR Print DEBUG level logging details to console .SH "COMMANDS" .TP \fBtuna isolate\fR usage: tuna-cmd.py isolate [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N) Move all allowed threads and IRQs away from CPU\-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR. optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations .TP \fBtuna include\fR usage: tuna-cmd.py include [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N) Allow all allowed threads and IRQs to run on CPU\-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR. optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations .TP \fBtuna move\fR usage: tuna-cmd.py move [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N) [-t THREAD-LIST] [-q IRQ-LIST] Move selected entities to CPU\-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR and \fB-t\fR or \fB-q\fR. optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations -t THREAD-LIST, --threads THREAD-LIST THREAD-LIST affected by commands -q IRQ-LIST, --irqs IRQ-LIST IRQ-LIST affect by commands .TP \fBtuna spread\fR usage: tuna-cmd.py spread [-h] (-c CPU-LIST | -S CPU-SOCKET-LIST | -N) [-t THREAD-LIST] [-q IRQ-LIST] Spread selected entities over CPU-LIST. The specified threads and IRQs are each assigned to one cpu in CPU-LIST. Requires \fB\-c\fR, \fB-S\fR, or \fB-N\fR and \fB-t\fR or \fB-q\fR. optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations -t THREAD-LIST, --threads THREAD-LIST THREAD-LIST affected by commands -q IRQ-LIST, --irqs IRQ-LIST IRQ-LIST affect by commands .TP \fBtuna priority\fR usage: tuna-cmd.py priority [-h] -t THREAD-LIST [-C] POLICY:RTPRIO Set thread scheduler tunables: POLICY and RTPRIO. POLICY is one of OTHER, FIFO, RR, or BATCH. Provide POLICY, RTPRIO, or POLICY:RTPRIO separated by ":". If only POLICY is set, the RT priority will default to 1 if the policy is RT, and 0 otherwise. If only RTPRIO is specified, policy will not be changed. positional arguments: POLICY:RTPRIO Set thread scheduler tunables: POLICY and RTPRIO optional arguments: -h, --help show this help message and exit -t THREAD-LIST, --threads THREAD-LIST THREAD-LIST affected by commands -C, --affect_children Operation will affect children threads .TP \fBtuna run\fR usage: tuna-cmd.py run [-h] [-c CPU-LIST | -S CPU-SOCKET-LIST | -N] [-p PRIORITY] [-b] COMMAND Run the COMMAND. The entire command line must be provided inside "quotes". Modifiers \fB-c\fR, \fB-S\fR and \fB-p\fR can be used to set the affinity and scheduler tunables of the given COMMAND. positional arguments: COMMAND fork a new process and run the "COMMAND" optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations -p PRIORITY, --priority PRIORITY Set thread scheduler tunables: POLICY and RTPRIO -b, --background Run command as background task .TP \fBtuna save\fR usage: tuna-cmd.py save [-h] [-c CPU-LIST | -S CPU-SOCKET-LIST | -N] [-t THREAD-LIST] FILENAME Save kthreads sched tunables to FILENAME positional arguments: FILENAME Save kthreads sched tunables to this file optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations -t THREAD-LIST, --threads THREAD-LIST THREAD-LIST affected by commands .TP \fBtuna apply\fR usage: tuna-cmd.py apply [-h] profilename Apply changes described in profile positional arguments: profilename Apply changes described in this file optional arguments: -h, --help show this help message and exit .TP \fBtuna show_threads\fR usage: tuna-cmd.py show_threads [-h] [-c CPU-LIST | -N | -S CPU-SOCKET-LIST] [-t THREAD-LIST | -q IRQ-LIST] [-U] [-K] [-C] [-G] Show thread list optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -t THREAD-LIST, --threads THREAD-LIST THREAD-LIST affected by commands -q IRQ-LIST, --irqs IRQ-LIST IRQ-LIST affect by commands -U, --no_uthreads Operations will not affect user threads -K, --no_kthreads Operations will not affect kernel threads -C, --affect_children Operation will affect children threads -G, --cgroups Display the processes with the type of cgroups they are in -z, --spaced Display spaced view for cgroups .TP \fBtuna show_irqs\fR usage: tuna-cmd.py show_irqs [-h] [-c CPU-LIST | -N | -S CPU-SOCKET-LIST] [-q IRQ-LIST] Show IRQ list optional arguments: -h, --help show this help message and exit -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -q IRQ-LIST, --irqs IRQ-LIST IRQ-LIST affect by commands .TP \fBtuna show_configs\fR usage: tuna-cmd.py show_configs [-h] List preloaded profiles optional arguments: -h, --help show this help message and exit .TP \fB tuna what_is\fR usage: tuna-cmd.py what_is [-h] THREAD-LIST Provides help about selected entities positional arguments: THREAD-LIST THREAD-LIST affected by commands optional arguments: -h, --help show this help message and exit .TP \fB tuna gui\fR usage: tuna-cmd.py gui [-h] [-d] [-R MSEC] [-c CPU-LIST | -N | -S CPU-SOCKET-LIST] [-U] [-K] Start the GUI optional arguments: -h, --help show this help message and exit -d, --disable_perf Explicitly disable usage of perf in GUI for process view -R MSEC, --refresh MSEC Refresh the GUI every MSEC milliseconds -c CPU-LIST, --cpus CPU-LIST CPU-LIST affected by commands -N, --nohz_full CPUs in nohz_full kernel command line will be affected by operations -S CPU-SOCKET-LIST, --sockets CPU-SOCKET-LIST CPU-SOCKET-LIST affected by commands -U, --no_uthreads Operations will not affect user threads -K, --no_kthreads Operations will not affect kernel threads .IP \fIModifiers\fR .TP \fB\-c\fR, \fB\-\-cpus\fR=\fICPU\-LIST\fR CPU\-LIST affected by commands. Requires a CPU number, a range, or a comma-separated list of CPU numbers. .TP \fB\-S\fR, \fB\-\-sockets\fR=\fICPU\-SOCKET\-LIST\fR CPU\-SOCKET\-LIST affected by commands. Requires a socket number or a comma-separated list of socket numbers. .TP \fB\-t\fR, \fB\-\-threads\fR=\fITHREAD\-LIST\fR THREAD\-LIST affected by commands. Requires a thread number or thread name, or a comma-separated list of thread numbers and/or names. Thread names may contain wildcards. Be sure to quote or escape any wildcard specifications. .TP .SH USAGE EXAMPLES If for instance the Ethernet NICs have multiple queues for both receive and transmit, each with its own IRQ, the Ethernet IRQs can be associated with a CPU socket: .TP .B tuna isolate -S 2 .TP .B tuna spread -q 'eth*' -S 2 Move everything off the CPUs in socket 2, then spread the IRQs for the Ethernet devices across those same CPUs. tuna-0.19/etc/000077500000000000000000000000001437350234000131665ustar00rootroot00000000000000tuna-0.19/etc/tuna.conf000066400000000000000000000000631437350234000150030ustar00rootroot00000000000000[global] root = /etc/tuna/ lastfile = example.conf tuna-0.19/etc/tuna/000077500000000000000000000000001437350234000141355ustar00rootroot00000000000000tuna-0.19/etc/tuna/example.conf000066400000000000000000000027661437350234000164520ustar00rootroot00000000000000#List of enabled categories [categories] kernel = Kernel scheduler vm = VM ipv4 = Network IPv4 ipv6 = Network IPv6 net = Network Core [kernel] kernel.sched_latency_ns = 1000,50000000, kernel.sched_min_granularity_ns = ,, kernel.sched_nr_migrate = 0,128, kernel.sched_rt_period_us = ,, kernel.sched_rt_runtime_us = 1000,2000000, kernel.sched_tunable_scaling = 0,10, kernel.sched_wakeup_granularity_ns = 1000,20000000, kernel.sched_migration_cost_ns = ,, kernel.sched_autogroup_enabled = 0,1, kernel.core_pattern = kernel.sem = [vm] vm.dirty_ratio = 0,100, vm.dirty_background_ratio = ,, vm.dirty_writeback_centisecs = ,, vm.dirty_expire_centisecs = ,, vm.laptop_mode = 0,5, vm.swappiness = 0,100, vm.max_map_count = ,, vm.memory_failure_early_kill = 0,1,0 [net] net.core.rmem_default = 100000,1000000, net.core.rmem_max = 100000,1000000, net.core.wmem_default = 100000,1000000, net.core.wmem_max = 100000,1000000, [ipv4] net.ipv4.tcp_window_scaling = 0,1, net.ipv4.conf.all.forwarding = 0,1, net.ipv4.conf.all.rp_filter = net.ipv4.tcp_congestion_control = net.ipv4.tcp_max_syn_backlog = net.ipv4.tcp_mem = net.ipv4.tcp_slow_start_after_idle = 0,1, [ipv6] net.ipv6.conf.*.forwarding = 0,1, #special section for gui alias [guiAlias] net.ipv4 = ipv4 net.ipv6 = ipv6 net.core = core #special section for this file description [fileDescription] text = This file contain some features for tunning kernel params.Mainly this is example file for demonstrate tuna new features. Params are set as default or most uses value tuna-0.19/help/000077500000000000000000000000001437350234000133435ustar00rootroot00000000000000tuna-0.19/help/kthreads/000077500000000000000000000000001437350234000151505ustar00rootroot00000000000000tuna-0.19/help/kthreads/events-000066400000000000000000000001071437350234000164520ustar00rootroot00000000000000Global workqueue, used to schedule work to be done in process context. tuna-0.19/help/kthreads/group_balance000066400000000000000000000000431437350234000176710ustar00rootroot00000000000000Scheduler load balance monitoring. tuna-0.19/help/kthreads/kblockd-000066400000000000000000000001271437350234000165610ustar00rootroot00000000000000Workqueue used to process IO requests. Used by IO schedulers and block device drivers. tuna-0.19/help/kthreads/khelper000066400000000000000000000001761437350234000165310ustar00rootroot00000000000000Used to call user mode helpers from the kernel, such as /sbin/bridge-stp, ocfs2_hb_ctl, pnpbios, poweroff, request-key, etc." tuna-0.19/help/kthreads/kjournald000066400000000000000000000011061437350234000170620ustar00rootroot00000000000000Main thread function used to manage a filesystem logging device journal. This kernel thread is responsible for two things: COMMIT: Every so often we need to commit the current state of the filesystem to disk. The journal thread is responsible for writing all of the metadata buffers to disk. CHECKPOINT: We cannot reuse a used section of the log file until all of the data in that part of the log has been rewritten elsewhere on the disk. Flushing these old buffers to reclaim space in the log is known as checkpointing, and this thread is responsible for that job. tuna-0.19/help/kthreads/kondemand-000066400000000000000000000002071437350234000171070ustar00rootroot00000000000000This cpufreq workqueue runs periodically to sample the idleness of the system, increasing or reducing the CPU frequency to save power. tuna-0.19/help/kthreads/krcupreemptd000066400000000000000000000013671437350234000176070ustar00rootroot00000000000000This should run at the lowest RT priority. With preemptible RCU, a loaded system may have tasks that hold RCU locks but have a high nice value. These tasks may be pushed off for seconds, and if the system is tight on memory, the RCU deferred freeing may not occur. The result can be drastic. The krcupreemptd is a daemon that runs just above SCHED_OTHER and wakes up once a second and performs a synchronize RCU. With RCU boosting, all those that hold RCU locks will inherit the priority of the krcupreemptd and wake up and release the RCU locks. This is only a concern for loaded systems and SCHED_OTHER tasks. If there is an issue of RT tasks starving out SCHED_OTHER tasks and causing problems with freeing memory, then the RT tasks are designed badly. tuna-0.19/help/kthreads/ksoftirqd-000066400000000000000000000001761437350234000171620ustar00rootroot00000000000000Activated when under heavy networking activity. Used to avoid monopolizing the CPUs doing just software interrupt processing. tuna-0.19/help/kthreads/kthreadd000066400000000000000000000001461437350234000166620ustar00rootroot00000000000000Used to create kernel threads via kthread_create(). It is the parent of all the other kernel threads. tuna-0.19/help/kthreads/lockd000066400000000000000000000000461437350234000161670ustar00rootroot00000000000000Locking arbiter for NFS on the system tuna-0.19/help/kthreads/migration-000066400000000000000000000001721437350234000171410ustar00rootroot00000000000000High priority system thread that performs thread migration by bumping thread off CPU then pushing onto another runqueue. tuna-0.19/help/kthreads/posix_cpu_timer000066400000000000000000000002111437350234000202760ustar00rootroot00000000000000Per-cpu thread that handles POSIX timer callbacks. Timer callbacks are bound to a cpu and are handled by these threads as per-cpu data. tuna-0.19/help/kthreads/rpciod-000066400000000000000000000000621437350234000164260ustar00rootroot00000000000000Handles Sun RPC network messages (mainly for NFS) tuna-0.19/help/kthreads/sirq-block-000066400000000000000000000002371437350234000172200ustar00rootroot00000000000000Called after a completion to a block device is made. Looking further into this call, I only see a couple of users. The SCSI driver uses this as well as cciss. tuna-0.19/help/kthreads/sirq-high-000066400000000000000000000005131437350234000170420ustar00rootroot00000000000000This is from a poor attempt to prioritize tasklets. Some tasklets wanted to run before anything else. Thus there were two tasklet softirqs made. tasklet_vec and tasklet_hi_vec. A driver writer could put their "critical" tasklets into the tasklet_hi_vec and it would run before other softirqs. This never really worked as intended. tuna-0.19/help/kthreads/sirq-net-rx-000066400000000000000000000007161437350234000173450ustar00rootroot00000000000000When receiving a packet the device will place the packet on a queue with its hard interrupt (threaded in RT). The sirq-net-rx is responsible for finding out what to do with the packet. It may forward it to another box if the current box is used as a router, or it will find the task the packet is for. If that task is currently waiting for the packet, the softirq might hand it off to that task and the task will handle the rest of the processing of the packet. tuna-0.19/help/kthreads/sirq-net-tx-000066400000000000000000000005461437350234000173500ustar00rootroot00000000000000This is the network transmit queue. Most of the time the network packets will be handled by the task that is sending the packets out, and doing so at the priority of that task. But if the protocol window or the network device queue is full, then the packets will be pushed off to later. The sirq-net-tx softirq is responsible for sending out these packets. tuna-0.19/help/kthreads/sirq-rcu-000066400000000000000000000002151437350234000167130ustar00rootroot00000000000000Pushes the RCU grace period along (if possible) and will handle dereferenced RCU callbacks, such as freeing structures after a grace period. tuna-0.19/help/kthreads/sirq-sched-000066400000000000000000000007271437350234000172200ustar00rootroot00000000000000Triggered when a rebalance of tasks is needed to CPU domains. This handles balancing of SCHED_OTHER tasks across CPUs. RT tasks balancing is done directly in schedule and wakeup paths. Runs at prio 1 because it needs to schedule above all SCHED_OTHER tasks. If the user has the same issue but doesn't mind having latencies against other kernel threads that run here, then its fine. But it should definitely be documented that PRIO 1 has other threads on it at boot up. tuna-0.19/help/kthreads/sirq-tasklet-000066400000000000000000000005131437350234000175720ustar00rootroot00000000000000Catch all for those devices that couldn't use softirqs directly and mostly made before work queues were around. The difference between a tasklet and a softirq is that the same tasklet can not run on two different CPUs at the same time. In this regard it acts like a "task" (hence the name "tasklet"). Various devices use tasklets. tuna-0.19/help/kthreads/sirq-timer-000066400000000000000000000004041437350234000172420ustar00rootroot00000000000000Basically the timer wheel. Things that add itself to the timer wheel timeouts will be handled by this softirq. Parts of the kernel that need timeouts will use this softirq (i.e. network timeouts). The resolution to these timeouts are defined by the HZ value. tuna-0.19/help/kthreads/usb-storage000066400000000000000000000011751437350234000173320ustar00rootroot00000000000000Per USB storage device virtual SCSI controller. Persistant across device insertion/removal, as is the SCSI node. This is done so that a device which is removed can be re-attached and be granted the same /dev node as before, creating persistance between connections of the target unit. Gets commands from the SCSI mid-layer and, after sanity checking several things, sends the command to the 'protocol' handler. This handler is responsible for re-writing the command (if necessary) into a form which the device will accept. For example, ATAPI devices do not support 6-byte commands. Thus, they must be re-written into 10-byte variants. tuna-0.19/help/kthreads/watchdog-000066400000000000000000000005231437350234000167500ustar00rootroot00000000000000Run briefly once per second to reset the softlockup timestamp. If this gets delayed for more than 60 seconds then a message will be printed. Use /proc/sys/kernel/hung_task_timeout_secs and /proc/sys/kernel/hung_task_check_count to control this behaviour. Setting /proc/sys/kernel/hung_task_timeout_secs to zero will disable this check. tuna-0.19/org.tuna.policy000066400000000000000000000012751437350234000153760ustar00rootroot00000000000000 Run Tuna as root Authentication is required to run Tuna auth_admin auth_admin auth_admin /usr/bin/tuna true tuna-0.19/oscilloscope-cmd.py000077500000000000000000000073071437350234000162360ustar00rootroot00000000000000#!/usr/bin/python3 # Arnaldo Carvalho de Melo # # Please check the tuna repository at: # http://git.kernel.org/?p=linux/kernel/git/acme/tuna.git;a=tree # For newer versions and to see it integrated with tuna # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA import getopt import sys import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk from tuna import oscilloscope def usage(): print('''Usage: oscilloscope [OPTIONS] -h, --help Give this help list -d, --delimiter=CHARACTER CHARACTER used as a delimiter [Default: :] -f, --field=FIELD FIELD to plot [Default: 2] -g, --geometry=GEOMETRY X geometry specification (see "X" man page) -m, --max_value=MAX_VALUE MAX_VALUE for the scale -M, --sample_multiplier=VALUE VALUE to multiply each sample -n, --noscale Do not scale when a sample is > MAX_SCALE -s, --nr_samples_on_screen=NR Show NR samples on screen -S, --snapshot_samples=NR Take NR samples, a snapshot and exit -u, --unit=TYPE Unit TYPE [Default: us] ''') def main(): try: opts, args = getopt.getopt(sys.argv[1:], "d:f:g:hM:m:ns:S:u:", ("geometry=", "help", "max_value=", "sample_multiplier=", "noscale", "nr_samples_on_screen=", "snapshot_samples=", "unit=")) except getopt.GetoptError as err: usage() print(str(err)) sys.exit(2) max_value = 250 sample_multiplier = 1 snapshot_samples = 0 delimiter = ':' field = 2 ylabel = "Latency" unitlabel = "us" geometry = None scale = True nr_samples_on_screen = 250 for o, a in opts: if o in ("-d", "--delimiter"): delimiter = a elif o in ("-f", "--field"): field = int(a) elif o in ("-g", "--geometry"): geometry = a elif o in ("-h", "--help"): usage() return elif o in ("-m", "--max_value"): max_value = int(a) elif o in ("-M", "--sample_multiplier"): sample_multiplier = float(a) elif o in ("-n", "--noscale"): scale = False elif o in ("-s", "--nr_samples_on_screen"): nr_samples_on_screen = int(a) elif o in ("-S", "--snapshot_samples"): snapshot_samples = int(a) elif o in ("-u", "--unit"): unitlabel = a o = oscilloscope.cyclictestoscope(max_value, snapshot_samples, nr_samples_on_screen=nr_samples_on_screen, delimiter=delimiter, field=field, ylabel="%s (%s)" % (ylabel, unitlabel), geometry=geometry, scale=scale, sample_multiplier=sample_multiplier) o.run() Gtk.main() if __name__ == '__main__': main() tuna-0.19/po/000077500000000000000000000000001437350234000130315ustar00rootroot00000000000000tuna-0.19/po/LINGUAS000066400000000000000000000000171437350234000140540ustar00rootroot00000000000000pt_BR ja zh_CN tuna-0.19/po/POTFILES.in000066400000000000000000000004641437350234000146120ustar00rootroot00000000000000tuna/__init__.py tuna/tuna_gui.glade tuna/tuna_gui.py tuna/sysfs.py tuna/oscilloscope.py tuna/tuna.py tuna/help.py tuna/config.py tuna/gui/__init__.py tuna/gui/util.py tuna/gui/cpuview.py tuna/gui/procview.py tuna/gui/irqview.py tuna/gui/profileview.py tuna/gui/commonview.py tuna-cmd.py oscilloscope-cmd.py tuna-0.19/po/ja.po000066400000000000000000000661361437350234000137770ustar00rootroot00000000000000# Japanese message translations for tuna # Copyright (C) 2009 THE tuna'S COPYRIGHT HOLDER # This file is distributed under the same license as the tuna package. # Satoru Satoh , 2009. # msgid "" msgstr "" "Project-Id-Version: tuna 0.8.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-09-30 13:09+0900\n" "PO-Revision-Date: 2009-09-30 14:23+0900\n" "Last-Translator: Satoru SATOH \n" "Language-Team: Japanese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: tuna/tuna_gui.glade:8 msgid "Tuna" msgstr "Tuna" #: tuna/tuna_gui.glade:111 msgid "Set Process Attributes" msgstr "プロセス属性を設定" #: tuna/tuna_gui.glade:140 msgid "_Just the selected thread" msgstr "選択したスレッドのみ(_J)" #: tuna/tuna_gui.glade:155 msgid "_All threads of the selected process" msgstr "選択したプロセスのすべてのスレッド(_A)" #: tuna/tuna_gui.glade:172 msgid "A_ll command lines matching the regex below:" msgstr "正規表現にマッチするすべてのコマンドライン(_L)" #: tuna/tuna_gui.glade:190 msgid "Set for these processes" msgstr "これらのプロセス群に設定" #: tuna/tuna_gui.glade:210 msgid "_Policy: " msgstr "ポリシー(_P): " #: tuna/tuna_gui.glade:241 msgid "_Scheduler priority:" msgstr "スケジューラポリシー(_S):" #: tuna/tuna_gui.glade:277 msgid "A_ffinity:" msgstr "アフィニティ(_F):" #: tuna/tuna_gui.glade:324 msgid "Command line rege_x:" msgstr "コマンドラインの正規表現(_X):" #: tuna/tuna_gui.glade:412 msgid "Set IRQ Attributes" msgstr "IRQ の属性を設定" #: tuna/tuna_gui.glade:489 msgid "A_ffinity" msgstr "アフィニティ(_F)" #: tuna/tuna_gui.glade:504 msgid "_Scheduler priority" msgstr "スケジューラポリシー(_S)" #: tuna/tuna_gui.glade:519 msgid "_Policy" msgstr "ポリシー(_P)" #: tuna/tuna_gui.glade:539 msgid "Set attributes for this IRQ:" msgstr "この IRQ の属性を設定:" #: tuna/tuna_gui.py:106 msgid "Root privilege required" msgstr "root 権限が必要です" #: tuna/tuna_gui.py:107 msgid "Some functions will not work without root privilege." msgstr "いくつかの機能は root 権限なしでは動きません。" #: tuna/tuna_gui.py:108 msgid "Do you want to continue?" msgstr "続けますか?" #: tuna/tuna.py:24 #, python-format msgid "Kernel Thread %(pid)d (%(cmdline)s):" msgstr "カーネルスレッド %(pid)d (%(cmdline)s):" #: tuna/tuna.py:27 #, python-format msgid "User Thread %(pid)d (%(cmdline)s):" msgstr "ユーザースレッド %(pid)d (%(cmdline)s):" #: tuna/tuna.py:181 tuna/tuna.py:206 #, python-format msgid "could not change %(pid)d affinity to %(new_affinity)s" msgstr "PID %(pid)d のアフィニティを %(new_affinity)s に変更できませんでした" #: tuna/help.py:10 msgid "" "Used to create kernel threads via kthread_create(). It is the parent of all " "the other kernel threads." msgstr "" "kthread_create() 経由でカーネルスレッドを生成するのに使われます。このスレッド" "は他のすべてのカーネルスレッドの親となります。" #: tuna/help.py:11 msgid "" "Per-cpu thread that handles POSIX timer callbacks. Timer callbacks are bound " "to a cpu and are handled by these threads as per-cpu data." msgstr "" "POSIX タイマーのコールバックを扱うための CPU 毎のスレッドです。タイマーコール" "バックは CPU に関連付けられ、これらのスレッドによってCPU 毎のデータとして処理" "されます。" #: tuna/help.py:12 msgid "" "This cpufreq workqueue runs periodically to sample the idleness of the " "system, increasing or reducing the CPU frequency to save power. \n" "[One per CPU]" msgstr "" "この cpufreq ワークキューはシステムのアイドルの程度をサンプリングするために定" "期的に実行され、電力節約のために CPU 周波数を上げ下げします。\n" "[CPU 毎に一つ]" #: tuna/help.py:13 msgid "" "Pushes the RCU grace period along (if possible) and will handle dereferenced " "RCU callbacks, such as freeing structures after a grace period. \n" "[One per CPU]" msgstr "" "RCU の猶予期間をもし可能なら遅めて、猶予期間後に構造体を開放するような逆参照" "された RCU コールバックを処理するようにします。\n" "[CPU 毎に一つ]" #: tuna/help.py:14 msgid "" "Used to call user mode helpers from the kernel, such as /sbin/bridge-stp, " "ocfs2_hb_ctl, pnpbios, poweroff, request-key, etc.\"" msgstr "" "/sbin/bridge-stp、ocfs2_hb_ctl、pnpbios、poweroff、request-key などといった" "ユーザーモードのヘルパーをカーネルから呼び出すために使われます。" #: tuna/help.py:15 msgid "Scheduler load balance monitoring." msgstr "スケジューラの負荷分散モニタリング" #: tuna/help.py:16 msgid "" "Main thread function used to manage a filesystem logging device journal. " "This kernel thread is responsible for two things: COMMIT: Every so " "often we need to commit the current state of the filesystem to disk. The " "journal thread is responsible for writing all of the metadata buffers to " "disk. CHECKPOINT: We cannot reuse a used section of the log file " "until all of the data in that part of the log has been rewritten elsewhere " "on the disk. Flushing these old buffers to reclaim space in the log is " "known as checkpointing, and this thread is responsible for that job." msgstr "" "ファイルシステムのロギングデバイスジャーナルを管理するために使われるメインの" "スレッドです。このカーネルスレッドは次の二つのことについて責任を持ちます: " "COMMIT: ファイルシステムの現在の状態は頻繁にディスクにコミットされる必" "要があります。ジャーナルスレッドはすべてのメタデータバッファをディスクに書き" "込むことについて責任を持ちます。CHECKPOINT: ログファイルの使用中のセク" "ションについてはその部分のすべてのデータがディスクのどこか他の場所に再度書き" "込まれるまで、再利用することはできません。ログのスペース確保のためにこれらの" "古いバッファをフラッシュすることはチェックポイントとして知られていますが、こ" "のスレッドはその仕事について責任を持っています。" #: tuna/help.py:17 msgid "Locking arbiter for NFS on the system" msgstr "システム上の NFS のためのロックの担い手" # 後半、意訳した. #: tuna/help.py:18 msgid "" "Triggered when a rebalance of tasks is needed to CPU domains. This handles " "balancing of SCHED_OTHER tasks across CPUs. RT tasks balancing is done " "directly in schedule and wakeup paths. Runs at prio 1 because it needs to " "schedule above all SCHED_OTHER tasks. If the user has the same issue but " "doesn\"t mind having latencies against other kernel threads that run here, " "then its fine. But it should definitely be documented that PRIO 1 has other " "threads on it at boot up. \n" "[One per CPU]" msgstr "" "CPU ドメインについてタスクの再分配が必要な場合に実行されます。これは複数の " "CPU にわたって SCHED_OTHER タスクの分配を処理します。RT タスクの分配はスケ" "ジュールと起床パスで直接行なわれます。すべてのSCHED_OTHER タスクをスケジュー" "ルしなくてはならないので、優先度 1 で実行されています。もし同じ問題を持ってい" "て、しかしこの優先度で実行されている他のカーネルスレッドに対してレイテンシが" "生じることを気にしないのなら、こうすることでうまくいくでしょう。しかし優先度 " "1 であっても起動したときから同じ優先度で他のスレッドが動いていることに注意が" "必要です。\n" "[CPU 毎に一つ]" # Needs review #: tuna/help.py:19 msgid "" "This is from a poor attempt to prioritize tasklets. Some tasklets wanted to " "run before anything else. Thus there were two tasklet softirqs made. " "tasklet_vec and tasklet_hi_vec. A driver writer could put their \"critical\" " "tasklets into the tasklet_hi_vec and it would run before other softirqs. " "This never really worked as intended. \n" "[One per CPU]" msgstr "" "これはタスクレットを優先度付けしようとするはかない試みです。いくつかのタスク" "レットは他より先に実行されるのを望んでいます。そういうわけで tasklet_vec と " "tasklet_hi_vec の二つのタスクレットソフトウェア割り込みがつくられました。ドラ" "イバ開発者は彼らの \"緊急度の高い\" タスクレットをtasklet_hi_vec におき、他の" "ソフトウェア割り込みよりも前に実行されるように望みます。しかし、決して意図さ" "れたとおりには動きません。\n" "[CPU 毎に一つ]" #: tuna/help.py:20 msgid "" "Workqueue used to process IO requests. Used by IO schedulers and block " "device drivers. \n" "[One per CPU]" msgstr "" "ワークキューは IO リクエストを処理するのに使われます。IO スケジューラとブロッ" "クデバイスドライバが利用します。\n" "[CPU 毎に一つ]" #: tuna/help.py:21 msgid "" "When receiving a packet the device will place the packet on a queue with its " "hard interrupt (threaded in RT). The sirq-net-rx is responsible for finding " "out what to do with the packet. It may forward it to another box if the " "current box is used as a router, or it will find the task the packet is for. " "If that task is currently waiting for the packet, the softirq might hand it " "off to that task and the task will handle the rest of the processing of the " "packet. \n" "[One per CPU]" msgstr "" "パケットを受信するとデバイスはハードウェア割り込み (RT スレッド) でパケットを" "キューに置きます。sirq-net-rx はそのパケットをどうすべきかみつけることに責任" "を持っています。ルーターなら他のサーバーに転送されるでしょうし、あるいはパ" "ケットのタスクをみつけるでしょう。もしタスクがそのときそのパケットを待ってい" "るのならソフトウェア割り込みはタスクに引き継ぎ、タスクはパケットの処理の残り" "の部分を実行します。\n" "[CPU 毎に一つ]" #: tuna/help.py:22 msgid "" "This should run at the lowest RT priority. With preemptible RCU, a loaded " "system may have tasks that hold RCU locks but have a high nice value. These " "tasks may be pushed off for seconds, and if the system is tight on memory, " "the RCU deferred freeing may not occur. The result can be drastic. The " "krcupreemptd is a daemon that runs just above SCHED_OTHER and wakes up once " "a second and performs a synchronize RCU. With RCU boosting, all those that " "hold RCU locks will inherit the priority of the krcupreemptd and wake up and " "release the RCU locks. This is only a concern for loaded systems and " "SCHED_OTHER tasks. If there is an issue of RT tasks starving out SCHED_OTHER " "tasks and causing problems with freeing memory, then the RT tasks are " "designed badly." msgstr "" "これはもっとも低い RT 優先度で実行されるべきです。プリエンプト可能なRCU では" "システムは RCU ロックを持つが高い nice 値を持つタスクが生じえます。これらタス" "クがしばらくスリープしてしまっていても、もしシステムのメモリが逼迫していれば " "RCU の遅延開放は起らないかもしれません。その結果は深刻です。krcupreemptd は" "SCHED_OTHER でちょうど動作し、一秒間に一回起きて RCU の同期を実行します。RCU " "の優先度を上げることで、すべてのこれら RCU ロックを保持するタスクは" "krcupreemptd の優先度を継承し、起床して RCU ロックを開放します。起動されたシ" "ステムと SCHED_OTHER タスクの唯一の心配はこれ (RCU のロック) だけです。RT タ" "スクの問題によって SCHED_OTHER タスクの資源が枯渇し、メモリを開放する上で問題" "が生じるのなら、それは RT タスクの設計が悪いからです。" #: tuna/help.py:23 msgid "" "Activated when under heavy networking activity. Used to avoid monopolizing " "the CPUs doing just software interrupt processing. \n" "[One per CPU]" msgstr "" "ネットワークの活動が重い場合にアクティブになります。ソフトウェア割り込みの処" "理のためだけに CPU が占有されることを防ぐために使われます。\n" "[CPU 毎に一つ]" #: tuna/help.py:24 msgid "" "Basically the timer wheel. Things that add itself to the timer wheel " "timeouts will be handled by this softirq. Parts of the kernel that need " "timeouts will use this softirq (i.e. network timeouts). The resolution to " "these timeouts are defined by the HZ value. \n" "[One per CPU]" msgstr "" "基本的にはタイマー処理を行います。タイマーに登録してタイムアウトするとこのソ" "フトウェア割り込みによって処理されます。例えばネットワークのタイムアウトなど" "タイムアウトを必要とするカーネル部分でこのソフトウェア割り込みが利用されま" "す。タイムアウトの時間分解能は HZ 値で定義されます。\n" "[CPU 毎に一つ]" #: tuna/help.py:25 msgid "" "Global workqueue, used to schedule work to be done in process context. \n" "[One per CPU]" msgstr "" "グローバルのワークキューであり、プロセスコンテキストで実行されるスケジュール" "処理に使われます。\n" "[CPU 毎に一つ]" #: tuna/help.py:26 msgid "" "Run briefly once per second to reset the softlockup timestamp. If this gets " "delayed for more than 60 seconds then a message will be printed. Use /proc/" "sys/kernel/hung_task_timeout_secs and /proc/sys/kernel/hung_task_check_count " "to control this behaviour. Setting /proc/sys/kernel/hung_task_timeout_secs " "to zero will disable this check. \n" "[One per CPU]" msgstr "" "ソフトロックアップタイムスタンプをリセットするために一秒間に一度起動されま" "す。60 秒以上遅らせられた場合はメッセージが出力されます。/proc/sys/kernel/" "hung_task_timeout_secs と /proc/sys/kernel/hung_task_check_count でふるまいを" "制御します。/proc/sys/kernel/hung_task_timeout_secs を 0 にするとこのチェック" "は無効となります。\n" "[CPU 毎に一つ]" #: tuna/help.py:27 msgid "" "This is the network transmit queue. Most of the time the network packets " "will be handled by the task that is sending the packets out, and doing so at " "the priority of that task. But if the protocol window or the network device " "queue is full, then the packets will be pushed off to later. The sirq-net-tx " "softirq is responsible for sending out these packets. \n" "[One per CPU]" msgstr "" "ネットワークの転送キューです。ほとんどの時間ネットワークパケットはこのタスク" "の優先度でこのタスクによって処理されて送出されています。しかしプロトコルウィ" "ンドウまたはネットワークデバイスキューが一杯になると、パケットは後で送出され" "るようになります。sirq-net-tx ソフトウェア割り込みはこれらのパケットの送信に" "責任を持っています。\n" "[CPU 毎に一つ]" #: tuna/help.py:28 msgid "" "Called after a completion to a block device is made. Looking further into " "this call, I only see a couple of users. The SCSI driver uses this as well " "as cciss. \n" "[One per CPU]" msgstr "" "ブロックデバイスで処理が完了すると呼び出されます。よく見るとこれを使っている" "ユーザーはごく少数だけです。SCSI ドライバは cciss と同じようにこれを使ってい" "ます。\n" "[CPU 毎に一つ]" #: tuna/help.py:29 msgid "" "Catch all for those devices that couldn\"t use softirqs directly and mostly " "made before work queues were around. The difference between a tasklet and a " "softirq is that the same tasklet can not run on two different CPUs at the " "same time. In this regard it acts like a \"task\" (hence the name \"tasklet" "\"). Various devices use tasklets. \n" "[One per CPU]" msgstr "" "ソフトウェア割り込みを直接使うことができず、ワークキューが活動する前にたいて" "い発生するデバイスのためのすべての事象をつかまえます。タスクレットとソフト" "ウェア割り込みの違いは同じタスクレットは同時に二つの異なる CPU で実行できない" "ということです。この点においてタスクレットは \"タスク\" (それ故に名前も \"タ" "スクレット\") のように振舞います。様々なデバイスがタスクレットを使っていま" "す。\n" "[CPU 毎に一つ]" #: tuna/help.py:30 msgid "" "Per USB storage device virtual SCSI controller. Persistant across device " "insertion/removal, as is the SCSI node. This is done so that a device which " "is removed can be re-attached and be granted the same /dev node as before, " "creating persistance between connections of the target unit. Gets commands " "from the SCSI mid-layer and, after sanity checking several things, sends the " "command to the \"protocol\" handler. This handler is responsible for re-" "writing the command (if necessary) into a form which the device will accept. " "For example, ATAPI devices do not support 6-byte commands. Thus, they must " "be re-written into 10-byte variants." msgstr "" "USB ストレージデバイス毎の仮想 SCSI コントローラ。SCSI ノードとしてデバイスの" "抜き差しを通して永続します。これはターゲットユニットの接続を通して永続的につ" "くられ、抜かれたデバイスが再度接続されたときに前と同じ /dev ノードとなるよう" "になります。SCSI 中間レイヤーからコマンドを受け取り、いくつか健全性チェックを" "行った後で、\"プロトコル\" ハンドラにそのコマンドを送ります。ハンドラは必要な" "らデバイスが受け取ることのできる形式にコマンドを変換する責任を担っています。" "例えば ATAPI デバイスは 6-byte コマンドをサポートしていませんので、10-byte の" "ものに変換してやらなければいけません。" #: tuna/help.py:31 msgid "" "High priority system thread that performs thread migration by bumping thread " "off CPU then pushing onto another runqueue. \n" "[One per CPU]" msgstr "" "スレッドを CPU から外し、他の実行キューに移動することで、スレッドマイグレー" "ションを実行する高い優先度のシステムスレッドです。\n" "[CPU 毎に一つ]" #: tuna/help.py:32 msgid "" "Handles Sun RPC network messages (mainly for NFS) \n" "[One per CPU]" msgstr "" "主に NFS のための Sun RPC ネットワークメッセージを処理します。\n" "[CPU 毎に一つ]" #: tuna/gui/util.py:50 msgid "Invalid affinity, specify a list of CPUs!" msgstr "不正なアフィニティです。CPU のリストを指定して下さい!" #: tuna/gui/util.py:69 msgid "Invalid parameters!" msgstr "不正なパラメータです!" #: tuna/gui/util.py:76 #, python-format msgid "" "couldn't change pid %(pid)d from %(cpol)s(%(cpri)d) to %(npol)s(%(npri)d)!" msgstr "" "PID %(pid)d について %(cpol)s(%(cpri)d) から %(npol)s(%(npri)d) に変更できま" "せんでした!" #: tuna/gui/util.py:110 #, python-format msgid "couldn't change pid %(pid)d from %(caff)s to %(naff)s!" msgstr "PID %(pid)d について %(caff)s から %(naff)s に変更できませんでした!" #: tuna/gui/cpuview.py:15 #, python-format msgid "Couldn't change the affinity of %(tid)d to %(affinity)s!" msgstr "%(tid)d のアフィニティを %(affinity)s に変更できませんでした!" #: tuna/gui/cpuview.py:46 #, python-format msgid "Socket %s" msgstr "ソケット %s" #: tuna/gui/cpuview.py:64 msgid "Filter" msgstr "フィルタ" #: tuna/gui/cpuview.py:68 msgid "CPU" msgstr "CPU" #: tuna/gui/cpuview.py:74 tuna/gui/cpuview.py:78 msgid "Usage" msgstr "消費率" #: tuna/gui/cpuview.py:197 msgid "I_nclude CPU" msgstr "CPU を含める(_N)" #: tuna/gui/cpuview.py:198 msgid "_Isolate CPU" msgstr "CPU を隔離(_I)" #: tuna/gui/cpuview.py:200 msgid "I_nclude CPU Socket" msgstr "CPU ソケットを含める(_N)" #: tuna/gui/cpuview.py:201 msgid "_Isolate CPU Socket" msgstr "CPU ソケットを隔離(_I)" #: tuna/gui/cpuview.py:202 msgid "_Restore CPU" msgstr "CPU を復帰させる(_R)" #: tuna/gui/procview.py:200 tuna/gui/procview.py:220 tuna/gui/irqview.py:118 msgid "PID" msgstr "PID" #: tuna/gui/procview.py:201 tuna/gui/procview.py:221 tuna/gui/irqview.py:119 msgid "Policy" msgstr "ポリシー" #: tuna/gui/procview.py:202 tuna/gui/procview.py:222 tuna/gui/irqview.py:120 msgid "Priority" msgstr "優先度" #: tuna/gui/procview.py:203 tuna/gui/procview.py:223 tuna/gui/irqview.py:121 #: tuna/gui/irqview.py:140 msgid "Affinity" msgstr "アフィニティ" #: tuna/gui/procview.py:204 msgid "VolCtxtSwitch" msgstr "VolCtxtSwitch" #: tuna/gui/procview.py:205 msgid "NonVolCtxtSwitch" msgstr "NonVolCtxtSwitch" #: tuna/gui/procview.py:206 tuna/gui/procview.py:224 msgid "Command Line" msgstr "コマンドライン" #: tuna/gui/procview.py:284 msgid "Kernel Thread" msgstr "カーネルスレッド" #: tuna/gui/procview.py:470 msgid "Save As" msgstr "名前を付けて保存" #: tuna/gui/procview.py:528 #, python-format msgid "Kernel thread tunings saved to %s!" msgstr "カーネルスレッド設定を %s に保存しました!" #: tuna/gui/procview.py:541 msgid "_Set process attributes" msgstr "プロセス属性を設定(_S)" #: tuna/gui/procview.py:543 msgid "Sto_p refreshing the process list" msgstr "プロセスリストの更新を停止(_P)" #: tuna/gui/procview.py:545 msgid "_Refresh the process list" msgstr "プロセスリストを更新(_R)" #: tuna/gui/procview.py:548 msgid "_Hide kernel threads" msgstr "カーネルスレッドを隠す(_H)" #: tuna/gui/procview.py:550 msgid "_Show kernel threads" msgstr "カーネルスレッドを表示(_S)" #: tuna/gui/procview.py:553 msgid "_Hide user threads" msgstr "ユーザースレッドを隠す(_H)" #: tuna/gui/procview.py:555 msgid "_Show user threads" msgstr "ユーザースレッドを表示(_S)" #: tuna/gui/procview.py:557 msgid "_What is this?" msgstr "これは何?(_W)" #: tuna/gui/procview.py:559 msgid "_Save kthreads tunings" msgstr "カーネルスレッド設定を保存(_S)" #: tuna/gui/irqview.py:117 tuna/gui/irqview.py:139 msgid "IRQ" msgstr "IRQ" #: tuna/gui/irqview.py:122 tuna/gui/irqview.py:141 msgid "Events" msgstr "Events" #: tuna/gui/irqview.py:123 tuna/gui/irqview.py:142 msgid "Users" msgstr "Users" #: tuna/gui/irqview.py:286 msgid "_Set IRQ attributes" msgstr "IRQ の属性を設定(_S)" #: tuna/gui/irqview.py:288 msgid "Sto_p refreshing the IRQ list" msgstr "IRQ リストの更新を停止(_P)" #: tuna/gui/irqview.py:290 msgid "_Refresh the IRQ list" msgstr "IRQ リストを更新(_R)" #: tuna-cmd.py:43 msgid "Usage: tuna [OPTIONS]" msgstr "使用法: tuna [OPTIONS]" #: tuna-cmd.py:45 msgid "Give this help list" msgstr "このヘルプリストを表示" #: tuna-cmd.py:46 msgid "Start the GUI" msgstr "GUI を起動" #: tuna-cmd.py:47 tuna-cmd.py:48 tuna-cmd.py:52 tuna-cmd.py:54 tuna-cmd.py:57 #: tuna-cmd.py:79 msgid "CPU-LIST" msgstr "CPU-LIST" #: tuna-cmd.py:47 #, python-format msgid "%(cpulist)s affected by commands" msgstr "%(cpulist)s をコマンドの対象とする" #: tuna-cmd.py:49 msgid "Operation will affect children threads" msgstr "子のスレッドも操作の影響下におく" #: tuna-cmd.py:50 msgid "Display filter the selected entities" msgstr "選択したエンティティをフィルタ表示" #: tuna-cmd.py:51 #, python-format msgid "Move all threads away from %(cpulist)s" msgstr "すべてのスレッドを %(cpulist)s から移動" #: tuna-cmd.py:53 #, python-format msgid "Allow all threads to run on %(cpulist)s" msgstr "すべてのスレッドについて %(cpulist)s で実行されることを許す" #: tuna-cmd.py:55 msgid "Operations will not affect kernel threads" msgstr "カーネルスレッドを操作の対象から外す" #: tuna-cmd.py:56 #, python-format msgid "Move selected entities to %(cpulist)s" msgstr "選択したエンティティを %(cpulist)s に移動" #: tuna-cmd.py:59 msgid "Show network sockets in use by threads" msgstr "スレッドが使用しているネットワークソケットを表示" #: tuna-cmd.py:61 tuna-cmd.py:63 msgid "POLICY" msgstr "POLICY" #: tuna-cmd.py:62 tuna-cmd.py:63 msgid "RTPRIO" msgstr "RTPRIO" #: tuna-cmd.py:62 #, python-format msgid "Set thread scheduler tunables: %(policy)s and %(rtprio)s" msgstr "スレッドのスケジューラパラメータを設定: %(policy)s と %(rtprio)s" #: tuna-cmd.py:64 msgid "Show thread list" msgstr "スレッドのリストを表示" #: tuna-cmd.py:65 tuna-cmd.py:66 msgid "IRQ-LIST" msgstr "IRQ-LIST" #: tuna-cmd.py:65 #, python-format msgid "%(irqlist)s affected by commands" msgstr "%(irqlist)s をコマンドの対象とする" #: tuna-cmd.py:67 tuna-cmd.py:68 msgid "FILENAME" msgstr "FILENAME" #: tuna-cmd.py:67 #, python-format msgid "Save kthreads sched tunables to %(filename)s" msgstr "カーネルスレッドのスケジューラパラメータ設定を %(filename)s に保存" #: tuna-cmd.py:70 tuna-cmd.py:71 msgid "CPU-SOCKET-LIST" msgstr "CPU-SOCKET-LIST" #: tuna-cmd.py:70 #, python-format msgid "%(cpusocketlist)s affected by commands" msgstr "%(cpusocketlist)s をコマンドの対象とする" #: tuna-cmd.py:73 tuna-cmd.py:74 msgid "THREAD-LIST" msgstr "THREAD-LIST" #: tuna-cmd.py:73 #, python-format msgid "%(threadlist)s affected by commands" msgstr "%(threadlist)s をコマンドの対象とする" #: tuna-cmd.py:75 msgid "Operations will not affect user threads" msgstr "ユーザースレッドを操作の対象から外す" #: tuna-cmd.py:76 msgid "Show version" msgstr "バージョンを表示" #: tuna-cmd.py:77 msgid "Provides help about selected entities" msgstr "選択したエンティティのヘルプを提供" #: tuna-cmd.py:78 #, python-format msgid "Spread selected entities over %(cpulist)s" msgstr "選択したエンティティを %(cpulist)s に展開" #: tuna-cmd.py:94 #, python-format msgid "thread %d doesn't exists!" msgstr "スレッド %d は存在しません!" #: tuna-cmd.py:113 msgid "thread" msgstr "スレッド" #: tuna-cmd.py:400 tuna-cmd.py:405 tuna-cmd.py:422 msgid "requires a cpu list!" msgstr "CPU リストが必要です!" #: tuna-cmd.py:425 msgid "requires a list of threads/irqs!" msgstr "スレッド/IRQ のリストが必要です!" #: tuna-cmd.py:451 #, python-format msgid "invalid socket %(socket)s sockets available: %(available)s" msgstr "不正なソケット %(socket)s, 利用可能なソケット: %(available)s" #: tuna-cmd.py:476 msgid "requires a thread list!" msgstr "スレッドリストが必要です!" tuna-0.19/po/pt_BR.po000066400000000000000000000433121437350234000144020ustar00rootroot00000000000000# tuna translation to brazilian portuguese. # Copyright (C) 2009 Red Hat Inc. # This file is distributed under the same license as the tuna package. # Arnaldo Carvalho de Melo , 2009. # Revision: Luis Claudio R. Goncalves # msgid "" msgstr "" "Project-Id-Version: tuna 0.9.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-11-12 13:18-0200\n" "PO-Revision-Date: 2009-11-12 13:19+BRT\n" "Last-Translator: Arnaldo Carvalho de Melo \n" "Language-Team: pt_BR \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: tuna/tuna_gui.glade:8 msgid "Tuna" msgstr "" #: tuna/tuna_gui.glade:111 msgid "Set Process Attributes" msgstr "Configurar Atributos do Processo" #: tuna/tuna_gui.glade:140 msgid "_Just the selected thread" msgstr "_Somente a thread selecionada" #: tuna/tuna_gui.glade:155 msgid "_All threads of the selected process" msgstr "_Todas as threads do processo selecionado" #: tuna/tuna_gui.glade:172 msgid "A_ll command lines matching the regex below:" msgstr "T_odas as linhas de comando combinando com a expressão regular abaixo:" #: tuna/tuna_gui.glade:190 msgid "Set for these processes" msgstr "Configure estes processos" #: tuna/tuna_gui.glade:210 msgid "_Policy: " msgstr "_Política: " #: tuna/tuna_gui.glade:241 msgid "_Scheduler priority:" msgstr "_Prioridade no escalonador" #: tuna/tuna_gui.glade:277 msgid "A_ffinity:" msgstr "A_finidade: " #: tuna/tuna_gui.glade:324 msgid "Command line rege_x:" msgstr "Rege_x para a linha de comando:" #: tuna/tuna_gui.glade:412 msgid "Set IRQ Attributes" msgstr "Configure atributos desta IRQ" #: tuna/tuna_gui.glade:489 msgid "A_ffinity" msgstr "A_finidade" #: tuna/tuna_gui.glade:504 msgid "_Scheduler priority" msgstr "Prioridade no E_scalonador" #: tuna/tuna_gui.glade:519 msgid "_Policy" msgstr "_Política" #: tuna/tuna_gui.glade:539 msgid "Set attributes for this IRQ:" msgstr "Configure attributos desta IRQ:" #: tuna/tuna_gui.py:106 msgid "Root privilege required" msgstr "Privilégios de super usuário são necessários" #: tuna/tuna_gui.py:107 msgid "Some functions will not work without root privilege." msgstr "Algumas partes não funcionarão sem privilégios de super usuário." #: tuna/tuna_gui.py:108 msgid "Do you want to continue?" msgstr "Deseja continuar?" #: tuna/tuna.py:24 #, python-format msgid "Kernel Thread %(pid)d (%(cmdline)s):" msgstr "Thread do Kernel %(pid)d (%(cmdline)s):" #: tuna/tuna.py:27 #, python-format msgid "User Thread %(pid)d (%(cmdline)s):" msgstr "Thread %(pid)d (%(cmdline)s):" #: tuna/tuna.py:188 tuna/tuna.py:213 #, python-format msgid "could not change %(pid)d affinity to %(new_affinity)s" msgstr "Não foi possível mudar a afinidade de %(pid)d para %(new_affinity)s" #: tuna/help.py:10 msgid "" "Used to create kernel threads via kthread_create(). It is the parent of all " "the other kernel threads." msgstr "" "Usado para criar threads do kernel via kthread_create. É o pai de todas as " "outras threads do kernel." #: tuna/help.py:11 msgid "" "Per-cpu thread that handles POSIX timer callbacks. Timer callbacks are bound " "to a cpu and are handled by these threads as per-cpu data." msgstr "" #: tuna/help.py:12 msgid "" "This cpufreq workqueue runs periodically to sample the idleness of the " "system, increasing or reducing the CPU frequency to save power. \n" "[One per CPU]" msgstr "" #: tuna/help.py:13 msgid "" "Pushes the RCU grace period along (if possible) and will handle dereferenced " "RCU callbacks, such as freeing structures after a grace period. \n" "[One per CPU]" msgstr "" #: tuna/help.py:14 msgid "" "Used to call user mode helpers from the kernel, such as /sbin/bridge-stp, " "ocfs2_hb_ctl, pnpbios, poweroff, request-key, etc.\"" msgstr "" #: tuna/help.py:15 msgid "Scheduler load balance monitoring." msgstr "" #: tuna/help.py:16 msgid "" "Main thread function used to manage a filesystem logging device journal. " "This kernel thread is responsible for two things: COMMIT: Every so " "often we need to commit the current state of the filesystem to disk. The " "journal thread is responsible for writing all of the metadata buffers to " "disk. CHECKPOINT: We cannot reuse a used section of the log file " "until all of the data in that part of the log has been rewritten elsewhere " "on the disk. Flushing these old buffers to reclaim space in the log is " "known as checkpointing, and this thread is responsible for that job." msgstr "" #: tuna/help.py:17 msgid "Locking arbiter for NFS on the system" msgstr "" #: tuna/help.py:18 msgid "" "Triggered when a rebalance of tasks is needed to CPU domains. This handles " "balancing of SCHED_OTHER tasks across CPUs. RT tasks balancing is done " "directly in schedule and wakeup paths. Runs at prio 1 because it needs to " "schedule above all SCHED_OTHER tasks. If the user has the same issue but " "doesn\"t mind having latencies against other kernel threads that run here, " "then its fine. But it should definitely be documented that PRIO 1 has other " "threads on it at boot up. \n" "[One per CPU]" msgstr "" #: tuna/help.py:19 msgid "" "This is from a poor attempt to prioritize tasklets. Some tasklets wanted to " "run before anything else. Thus there were two tasklet softirqs made. " "tasklet_vec and tasklet_hi_vec. A driver writer could put their \"critical\" " "tasklets into the tasklet_hi_vec and it would run before other softirqs. " "This never really worked as intended. \n" "[One per CPU]" msgstr "" #: tuna/help.py:20 msgid "" "Workqueue used to process IO requests. Used by IO schedulers and block " "device drivers. \n" "[One per CPU]" msgstr "" #: tuna/help.py:21 msgid "" "When receiving a packet the device will place the packet on a queue with its " "hard interrupt (threaded in RT). The sirq-net-rx is responsible for finding " "out what to do with the packet. It may forward it to another box if the " "current box is used as a router, or it will find the task the packet is for. " "If that task is currently waiting for the packet, the softirq might hand it " "off to that task and the task will handle the rest of the processing of the " "packet. \n" "[One per CPU]" msgstr "" #: tuna/help.py:22 msgid "" "This should run at the lowest RT priority. With preemptible RCU, a loaded " "system may have tasks that hold RCU locks but have a high nice value. These " "tasks may be pushed off for seconds, and if the system is tight on memory, " "the RCU deferred freeing may not occur. The result can be drastic. The " "krcupreemptd is a daemon that runs just above SCHED_OTHER and wakes up once " "a second and performs a synchronize RCU. With RCU boosting, all those that " "hold RCU locks will inherit the priority of the krcupreemptd and wake up and " "release the RCU locks. This is only a concern for loaded systems and " "SCHED_OTHER tasks. If there is an issue of RT tasks starving out SCHED_OTHER " "tasks and causing problems with freeing memory, then the RT tasks are " "designed badly." msgstr "" #: tuna/help.py:23 msgid "" "Activated when under heavy networking activity. Used to avoid monopolizing " "the CPUs doing just software interrupt processing. \n" "[One per CPU]" msgstr "" #: tuna/help.py:24 msgid "" "Basically the timer wheel. Things that add itself to the timer wheel " "timeouts will be handled by this softirq. Parts of the kernel that need " "timeouts will use this softirq (i.e. network timeouts). The resolution to " "these timeouts are defined by the HZ value. \n" "[One per CPU]" msgstr "" #: tuna/help.py:25 msgid "" "Global workqueue, used to schedule work to be done in process context. \n" "[One per CPU]" msgstr "" #: tuna/help.py:26 msgid "" "Run briefly once per second to reset the softlockup timestamp. If this gets " "delayed for more than 60 seconds then a message will be printed. Use /proc/" "sys/kernel/hung_task_timeout_secs and /proc/sys/kernel/hung_task_check_count " "to control this behaviour. Setting /proc/sys/kernel/hung_task_timeout_secs " "to zero will disable this check. \n" "[One per CPU]" msgstr "" #: tuna/help.py:27 msgid "" "This is the network transmit queue. Most of the time the network packets " "will be handled by the task that is sending the packets out, and doing so at " "the priority of that task. But if the protocol window or the network device " "queue is full, then the packets will be pushed off to later. The sirq-net-tx " "softirq is responsible for sending out these packets. \n" "[One per CPU]" msgstr "" #: tuna/help.py:28 msgid "" "Called after a completion to a block device is made. Looking further into " "this call, I only see a couple of users. The SCSI driver uses this as well " "as cciss. \n" "[One per CPU]" msgstr "" #: tuna/help.py:29 msgid "" "Catch all for those devices that couldn\"t use softirqs directly and mostly " "made before work queues were around. The difference between a tasklet and a " "softirq is that the same tasklet can not run on two different CPUs at the " "same time. In this regard it acts like a \"task\" (hence the name \"tasklet" "\"). Various devices use tasklets. \n" "[One per CPU]" msgstr "" #: tuna/help.py:30 msgid "" "Per USB storage device virtual SCSI controller. Persistant across device " "insertion/removal, as is the SCSI node. This is done so that a device which " "is removed can be re-attached and be granted the same /dev node as before, " "creating persistance between connections of the target unit. Gets commands " "from the SCSI mid-layer and, after sanity checking several things, sends the " "command to the \"protocol\" handler. This handler is responsible for re-" "writing the command (if necessary) into a form which the device will accept. " "For example, ATAPI devices do not support 6-byte commands. Thus, they must " "be re-written into 10-byte variants." msgstr "" #: tuna/help.py:31 msgid "" "High priority system thread that performs thread migration by bumping thread " "off CPU then pushing onto another runqueue. \n" "[One per CPU]" msgstr "" #: tuna/help.py:32 msgid "" "Handles Sun RPC network messages (mainly for NFS) \n" "[One per CPU]" msgstr "" #: tuna/gui/util.py:50 msgid "Invalid affinity, specify a list of CPUs!" msgstr "Afinidade inválida, especifique uma lista de CPUs!" #: tuna/gui/util.py:69 msgid "Invalid parameters!" msgstr "Parâmetros inválidos" #: tuna/gui/util.py:76 #, python-format msgid "" "couldn't change pid %(pid)d from %(cpol)s(%(cpri)d) to %(npol)s(%(npri)d)!" msgstr "" "não foi possível mudar o pid %(pid)d de %(cpol)s(%(cpri)d) para %(npol)s(%" "(npri)d)!" #: tuna/gui/util.py:110 #, python-format msgid "couldn't change pid %(pid)d from %(caff)s to %(naff)s!" msgstr "não foi possível mudar o pid %(pid)d de %(caff)s para %(naff)s!" #: tuna/gui/cpuview.py:15 #, python-format msgid "Couldn't change the affinity of %(tid)d to %(affinity)s!" msgstr "Não foi possível mudar a affinidade de %(tid)d para %(affinity)s!" #: tuna/gui/cpuview.py:46 #, python-format msgid "Socket %s" msgstr "Soquete %s" #: tuna/gui/cpuview.py:64 msgid "Filter" msgstr "Filtro" #: tuna/gui/cpuview.py:68 msgid "CPU" msgstr "" #: tuna/gui/cpuview.py:74 tuna/gui/cpuview.py:78 msgid "Usage" msgstr "Uso" #: tuna/gui/cpuview.py:197 msgid "I_nclude CPU" msgstr "I_ncluir CPU" #: tuna/gui/cpuview.py:198 msgid "_Isolate CPU" msgstr "_Isolar CPU" #: tuna/gui/cpuview.py:200 msgid "I_nclude CPU Socket" msgstr "I_nclua Soquete de CPU" #: tuna/gui/cpuview.py:201 msgid "_Isolate CPU Socket" msgstr "_Isole Soquete de CPU" #: tuna/gui/cpuview.py:202 msgid "_Restore CPU" msgstr "_Restaure CPU" #: tuna/gui/procview.py:200 tuna/gui/procview.py:220 tuna/gui/irqview.py:120 msgid "PID" msgstr "" #: tuna/gui/procview.py:201 tuna/gui/procview.py:221 tuna/gui/irqview.py:121 msgid "Policy" msgstr "Política" #: tuna/gui/procview.py:202 tuna/gui/procview.py:222 tuna/gui/irqview.py:122 msgid "Priority" msgstr "Prioridade" #: tuna/gui/procview.py:203 tuna/gui/procview.py:223 tuna/gui/irqview.py:123 #: tuna/gui/irqview.py:142 msgid "Affinity" msgstr "Afinidade" #: tuna/gui/procview.py:204 msgid "VolCtxtSwitch" msgstr "" "Trocas de\n" "Contexto\n" "Voluntárias" #: tuna/gui/procview.py:205 msgid "NonVolCtxtSwitch" msgstr "" "Trocas de\n" "Contexto\n" "Involuntárias" #: tuna/gui/procview.py:206 tuna/gui/procview.py:224 msgid "Command Line" msgstr "Linha de Comando" #: tuna/gui/procview.py:284 msgid "Kernel Thread" msgstr "Thread do Kernel" #: tuna/gui/procview.py:470 msgid "Save As" msgstr "Salvar Como" #: tuna/gui/procview.py:528 #, python-format msgid "Kernel thread tunings saved to %s!" msgstr "Configurações das threads do kernel salvas no arquivo %s!" #: tuna/gui/procview.py:541 msgid "_Set process attributes" msgstr "Configurar atributo_s do processo" #: tuna/gui/procview.py:543 msgid "Sto_p refreshing the process list" msgstr "_Parar de atualizar a lista de processos" #: tuna/gui/procview.py:545 msgid "_Refresh the process list" msgstr "Atualiza_r a lista de processos" #: tuna/gui/procview.py:548 msgid "_Hide kernel threads" msgstr "_Não mostrar threads do kernel" #: tuna/gui/procview.py:550 msgid "_Show kernel threads" msgstr "Mostrar thread_s do kernel" #: tuna/gui/procview.py:553 msgid "_Hide user threads" msgstr "Não mostrar threads" #: tuna/gui/procview.py:555 msgid "_Show user threads" msgstr "_Mostrar threads" #: tuna/gui/procview.py:557 msgid "_What is this?" msgstr "_O que é isto?" #: tuna/gui/procview.py:559 msgid "_Save kthreads tunings" msgstr "_Salvar" #: tuna/gui/irqview.py:119 tuna/gui/irqview.py:141 msgid "IRQ" msgstr "" #: tuna/gui/irqview.py:124 tuna/gui/irqview.py:143 msgid "Events" msgstr "Eventos" #: tuna/gui/irqview.py:125 tuna/gui/irqview.py:144 msgid "Users" msgstr "Usuários" #: tuna/gui/irqview.py:289 msgid "_Set IRQ attributes" msgstr "_Configure atributo_s da IRQ" #: tuna/gui/irqview.py:291 msgid "Sto_p refreshing the IRQ list" msgstr "_Pare de atualizar a lista de IRQs" #: tuna/gui/irqview.py:293 msgid "_Refresh the IRQ list" msgstr "_Atualize a lista de I_RQs" #: tuna-cmd.py:43 msgid "Usage: tuna [OPTIONS]" msgstr "Uso: tuna [OPÇÕES]" #: tuna-cmd.py:45 msgid "Give this help list" msgstr "Mostra esta tela de ajuda" #: tuna-cmd.py:46 msgid "Start the GUI" msgstr "Iniciar a interface gráfica" #: tuna-cmd.py:47 tuna-cmd.py:48 tuna-cmd.py:52 tuna-cmd.py:54 tuna-cmd.py:57 #: tuna-cmd.py:80 msgid "CPU-LIST" msgstr "LISTA-DE-CPUS" #: tuna-cmd.py:47 #, python-format msgid "%(cpulist)s affected by commands" msgstr "%(cpulist)s afetada por comandos" #: tuna-cmd.py:49 msgid "Operation will affect children threads" msgstr "A operação afetará as threads criadas por esta" #: tuna-cmd.py:50 msgid "Display filter the selected entities" msgstr "Filtre os elementos selecionados" #: tuna-cmd.py:51 #, python-format msgid "Move all threads away from %(cpulist)s" msgstr "Remova todas as threads de %(cpulist)s" #: tuna-cmd.py:53 #, python-format msgid "Allow all threads to run on %(cpulist)s" msgstr "Permita que todas as threads executem em %(cpulist)s" #: tuna-cmd.py:55 msgid "Operations will not affect kernel threads" msgstr "Operações não afetarão threads do kernel" #: tuna-cmd.py:56 #, python-format msgid "Move selected entities to %(cpulist)s" msgstr "Mova os elementos selecionados para %(cpulist)s" #: tuna-cmd.py:59 msgid "Show network sockets in use by threads" msgstr "Mostre a lista de sockets de rede em uso pelas threads" #: tuna-cmd.py:61 tuna-cmd.py:63 msgid "POLICY" msgstr "POLÍTICA" #: tuna-cmd.py:62 tuna-cmd.py:63 msgid "RTPRIO" msgstr "PRIORIDADE-RT" #: tuna-cmd.py:62 #, python-format msgid "Set thread scheduler tunables: %(policy)s and %(rtprio)s" msgstr "" "Configure a prioridade para %(rtprio)s e o escalonador para %(policy)s:" #: tuna-cmd.py:64 msgid "Show thread list" msgstr "Mostra lista de threads" #: tuna-cmd.py:65 msgid "Show IRQ list" msgstr "Mostra lista de IRQs" #: tuna-cmd.py:66 tuna-cmd.py:67 msgid "IRQ-LIST" msgstr "LISTA-DE-IRQS" #: tuna-cmd.py:66 #, python-format msgid "%(irqlist)s affected by commands" msgstr "%(irqlist)s afetada por comandos" #: tuna-cmd.py:68 tuna-cmd.py:69 msgid "FILENAME" msgstr "ARQUIVO" #: tuna-cmd.py:68 #, python-format msgid "Save kthreads sched tunables to %(filename)s" msgstr "Salva opções configuráveis das threads do kernel em %(filename)s" #: tuna-cmd.py:71 tuna-cmd.py:72 msgid "CPU-SOCKET-LIST" msgstr "LISTA-DE-SOCKETS-DE-CPUS" #: tuna-cmd.py:71 #, python-format msgid "%(cpusocketlist)s affected by commands" msgstr "%(cpusocketlist)s afetada por comandos" #: tuna-cmd.py:74 tuna-cmd.py:75 msgid "THREAD-LIST" msgstr "LISTA-DE-THREADS" #: tuna-cmd.py:74 #, python-format msgid "%(threadlist)s affected by commands" msgstr "%(threadlist)s afetada por comandos" #: tuna-cmd.py:76 msgid "Operations will not affect user threads" msgstr "Operações não afetarão threads de usuário" #: tuna-cmd.py:77 msgid "Show version" msgstr "Mostra número de versão" #: tuna-cmd.py:78 msgid "Provides help about selected entities" msgstr "Mostra ajuda sobre os elementos selecionados" #: tuna-cmd.py:79 #, python-format msgid "Spread selected entities over %(cpulist)s" msgstr "Espalhe os elementos selecionados em %(cpulist)s" #: tuna-cmd.py:104 #, python-format msgid "thread %d doesn't exists!" msgstr "thread %d não existe!" #: tuna-cmd.py:123 msgid "thread" msgstr "" #: tuna-cmd.py:321 msgid "users" msgstr "usuários" #: tuna-cmd.py:321 msgid "affinity" msgstr "afinidade" #: tuna-cmd.py:463 tuna-cmd.py:468 tuna-cmd.py:491 msgid "requires a cpu list!" msgstr "exige uma lista de cpus!" #: tuna-cmd.py:494 msgid "requires a list of threads/irqs!" msgstr "exige uma lista de threads/irqs!" #: tuna-cmd.py:520 #, python-format msgid "invalid socket %(socket)s sockets available: %(available)s" msgstr "" "soquete de CPU %(socket)s inválido, soquetes disponíveis: %(available)s" #: tuna-cmd.py:545 msgid "requires a thread list!" msgstr "requer uma lista de threads!" tuna-0.19/po/zh_CN.po000066400000000000000000000550071437350234000144010ustar00rootroot00000000000000# Simpified Chinese message translations for tuna # Copyright (C) 2009 THE tuna'S COPYRIGHT HOLDER # This file is distributed under the same license as the tuna package. # John Lau , 2009. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tuna 0.8.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-09-02 15:42+0100\n" "PO-Revision-Date: 2009-09-06 23:54+0800\n" "Last-Translator: John Lau \n" "Language-Team: Simplified Chinese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: tuna/tuna_gui.glade:8 msgid "Tuna" msgstr "Tuna" #: tuna/tuna_gui.glade:111 msgid "Set Process Attributes" msgstr "设置进程属性" #: tuna/tuna_gui.glade:140 msgid "_Just the selected thread" msgstr "调整选中进程(_J)" #: tuna/tuna_gui.glade:155 msgid "_All threads of the selected process" msgstr "程序的所有线程(_A)" #: tuna/tuna_gui.glade:172 msgid "A_ll command lines matching the regex below:" msgstr "所有匹配正则表达式的命令如下(_l):" #: tuna/tuna_gui.glade:190 msgid "Set for these processes" msgstr "设置这些进程" #: tuna/tuna_gui.glade:210 msgid "_Policy: " msgstr "规则(_P)" #: tuna/tuna_gui.glade:241 msgid "_Scheduler priority:" msgstr "调度程序优先级(_S)" #: tuna/tuna_gui.glade:277 msgid "A_ffinity:" msgstr "Affinity(_f)" #: tuna/tuna_gui.glade:324 msgid "Command line rege_x:" msgstr "命令行正则表达式(_x)" #: tuna/tuna_gui.glade:412 msgid "Set IRQ Attributes" msgstr "设置中断请求特征" #: tuna/tuna_gui.glade:489 msgid "A_ffinity" msgstr "Affinity(_f)" #: tuna/tuna_gui.glade:504 msgid "_Scheduler priority" msgstr "调度程序优先级(_S)" #: tuna/tuna_gui.glade:519 msgid "_Policy" msgstr "规则" #: tuna/tuna_gui.glade:539 msgid "Set attributes for this IRQ:" msgstr "设置这个中断请求的特征" #: tuna/tuna_gui.py:106 msgid "Root privilege required" msgstr "需要根用户权限" #: tuna/tuna_gui.py:107 msgid "Some functions will not work without root privilege." msgstr "没有根用户权限不能执行某些功能" #: tuna/tuna_gui.py:108 msgid "Do you want to continue?" msgstr "您要继续吗?" #: tuna/tuna.py:24 #, python-format msgid "Kernel Thread %(pid)d (%(cmdline)s):" msgstr "内核线程 %(pid)d (%(cmdline)s):" #: tuna/tuna.py:27 #, python-format msgid "User Thread %(pid)d (%(cmdline)s):" msgstr "用户进程 %(pid)d (%(cmdline)s):" #: tuna/tuna.py:188 tuna/tuna.py:213 #, python-format msgid "could not change %(pid)d affinity to %(new_affinity)s" msgstr "不能更改%(pid)d的affinity到%(new_affinity)s" #: tuna/help.py:10 msgid "" "Used to create kernel threads via kthread_create(). It is the parent of all " "the other kernel threads." msgstr "用于通过kthread_create()函数创建内核线程,它是所有其他内核线程的Parent" #: tuna/help.py:11 msgid "" "Per-cpu thread that handles POSIX timer callbacks. Timer callbacks are bound " "to a cpu and are handled by these threads as per-cpu data." msgstr "" "这线程要处理POSIX计时器回调,每CPU一个。计时器回调都是针对特定CPU。" #: tuna/help.py:12 msgid "" "This cpufreq workqueue runs periodically to sample the idleness of the " "system, increasing or reducing the CPU frequency to save power. \n" "[One per CPU]" msgstr "" "这个cpufreq工作队列定期运行对系统空闲进行抽样,增加或减小CPU的频率来节电.\n" "【每个CPU一个】" #: tuna/help.py:13 msgid "" "Pushes the RCU grace period along (if possible) and will handle dereferenced " "RCU callbacks, such as freeing structures after a grace period. \n" "[One per CPU]" msgstr "" "延长RCU的等待时间(如可以)和处理dereferenced RCU回调,例如在等待时间后释放的结构。" "【每个CPU一个】" #: tuna/help.py:14 msgid "" "Used to call user mode helpers from the kernel, such as /sbin/bridge-stp, " "ocfs2_hb_ctl, pnpbios, poweroff, request-key, etc.\"" msgstr "" "用来从内核调用用户模式助手,例如/sbin/bridge-stp,ocfs2_hb_ctl, pnpbios, " "poweroff, request-key,等等\"" #: tuna/help.py:15 msgid "Scheduler load balance monitoring." msgstr "调度程序负载平衡监视器" #: tuna/help.py:16 msgid "" "Main thread function used to manage a filesystem logging device journal. " "This kernel thread is responsible for two things: COMMIT: Every so " "often we need to commit the current state of the filesystem to disk. The " "journal thread is responsible for writing all of the metadata buffers to " "disk. CHECKPOINT: We cannot reuse a used section of the log file " "until all of the data in that part of the log has been rewritten elsewhere " "on the disk. Flushing these old buffers to reclaim space in the log is " "known as checkpointing, and this thread is responsible for that job." msgstr "" "主线程函数用来管理一个记录设备日志的文件系统.这个内核线程负责两件事:COMMIT:我们常需要将文件系统的当前状态提交到磁盘.日志线程负责将元数据缓冲区中的所有" "数据写入磁盘.CHECKPOINT:直到日志文件上被使用的一个段上面的所有数据都已经被重新写回到磁盘上,我们才能重新使用它。" "冲刷旧的缓存来回收日志空间,我们把它叫做checkpointing,这个线程就是用来负责这个工作的." #: tuna/help.py:17 msgid "Locking arbiter for NFS on the system" msgstr "锁定系统上NFS的判优器" #: tuna/help.py:18 msgid "" "Triggered when a rebalance of tasks is needed to CPU domains. This handles " "balancing of SCHED_OTHER tasks across CPUs. RT tasks balancing is done " "directly in schedule and wakeup paths. Runs at prio 1 because it needs to " "schedule above all SCHED_OTHER tasks. If the user has the same issue but " "doesn\"t mind having latencies against other kernel threads that run here, " "then its fine. But it should definitely be documented that PRIO 1 has other " "threads on it at boot up. \n" "[One per CPU]" msgstr "" "当CPU域需要任务重新均衡时这个任务被触发.这个线程处理SCHED_OTHER任务在CPU之间的均衡," "实时任务的平衡在进程调度和唤醒时直接完成.这个任务以优先级1来运行,是因为它需要优先於所有" "的SCHED_OTHER任务来调度.如果用户有相同的问题,但是并不关心与这里其他运行的内核线程的" "潜在关系,那么就没问题.但应明确地写明在启动的时候有另一些线程以优先级1运行" "【每个CPU一个】" #: tuna/help.py:19 msgid "" "This is from a poor attempt to prioritize tasklets. Some tasklets wanted to " "run before anything else. Thus there were two tasklet softirqs made. " "tasklet_vec and tasklet_hi_vec. A driver writer could put their \"critical\" " "tasklets into the tasklet_hi_vec and it would run before other softirqs. " "This never really worked as intended. \n" "[One per CPU]" msgstr "" "这是由于按优先级排列tasklet的失败尝试的结果。一些tasklet需要提前运行" "因此又有两个tasklet软中断:tasklet_vec和tasklet_hi_vec。一个驱动" "复写器可以将它们的\"critical\"tasklet放入tasklet_hi_vec,那么它将运行在其他软" "中断之前。这个不会起到预期的作用。【每个CPU一个】" #: tuna/help.py:20 msgid "" "Workqueue used to process IO requests. Used by IO schedulers and block " "device drivers. \n" "[One per CPU]" msgstr "这是处理IO请求的工作队列。被IO调度器和块设备驱动调用【每个CPU一个】" #: tuna/help.py:21 msgid "" "When receiving a packet the device will place the packet on a queue with its " "hard interrupt (threaded in RT). The sirq-net-rx is responsible for finding " "out what to do with the packet. It may forward it to another box if the " "current box is used as a router, or it will find the task the packet is for. " "If that task is currently waiting for the packet, the softirq might hand it " "off to that task and the task will handle the rest of the processing of the " "packet. \n" "[One per CPU]" msgstr "" "每当收到一个包,设备就会将这个包和它的硬中断一起放进一个队列。sirq-net-rx " "负责决定将要对包做什么处理。如果这个机器被用作一个路由器,它可能被转发,或者它将会发现" "包所要做的任务。如果某个任务正在等待某个" "包,那么软中断可能将它传送给那个任务,那个任务将会继续处理这个包。【每" "个CPU一个】" #: tuna/help.py:22 msgid "" "This should run at the lowest RT priority. With preemptible RCU, a loaded " "system may have tasks that hold RCU locks but have a high nice value. These " "tasks may be pushed off for seconds, and if the system is tight on memory, " "the RCU deferred freeing may not occur. The result can be drastic. The " "krcupreemptd is a daemon that runs just above SCHED_OTHER and wakes up once " "a second and performs a synchronize RCU. With RCU boosting, all those that " "hold RCU locks will inherit the priority of the krcupreemptd and wake up and " "release the RCU locks. This is only a concern for loaded systems and " "SCHED_OTHER tasks. If there is an issue of RT tasks starving out SCHED_OTHER " "tasks and causing problems with freeing memory, then the RT tasks are " "designed badly." msgstr "" "这个应该运行在最低的RT优先级上。因为有先占型的RCU,一个高负荷的系统可能" "有高nice值的任务持有RCU锁。这些任务可能被延时几秒钟,如果系统" "的内存已经剩余不多,那么RCU延迟的内存释放将引致严重后果。" "krcupreemptd是一个守护进程,仅当每秒被唤醒一次时运行在SCHED_OTHER之上,执行一" "个同步的RCU。 对已RCU的加速,所有那些管理RCU锁的任务都会" "继承krcupreemptd的优先级,唤醒和释放RCU锁。这只是针对对高负载的系统和" "SCHED_OTHER任务。如果RT任务因为SCHED_OTHER任务而饿死,以导致内存" "释放的问题发生,那么那个RT任务设计就是有问题。【每个CPU一个】" #: tuna/help.py:23 msgid "" "Activated when under heavy networking activity. Used to avoid monopolizing " "the CPUs doing just software interrupt processing. \n" "[One per CPU]" msgstr "在繁忙的网络上被激活。用于避免软中断独占CPU。" #: tuna/help.py:24 msgid "" "Basically the timer wheel. Things that add itself to the timer wheel " "timeouts will be handled by this softirq. Parts of the kernel that need " "timeouts will use this softirq (i.e. network timeouts). The resolution to " "these timeouts are defined by the HZ value. \n" "[One per CPU]" msgstr "" "基本的定时器轮转。事物会将自己加入定时器轮转中,超时设定将由软中断处理。那些" "需要超时设定的部分内核将使用这个软中断(比如网络超时设定)。对于这些超时设定" "的解决方法是由HZ的值定义的。【每个CPU一个】" #: tuna/help.py:25 msgid "" "Global workqueue, used to schedule work to be done in process context. \n" "[One per CPU]" msgstr "" "全局工作队列用于按照预定的时间将作业完成在进程的上下文中。【每个CPU一个】" #: tuna/help.py:26 msgid "" "Run briefly once per second to reset the softlockup timestamp. If this gets " "delayed for more than 60 seconds then a message will be printed. Use /proc/" "sys/kernel/hung_task_timeout_secs and /proc/sys/kernel/hung_task_check_count " "to control this behaviour. Setting /proc/sys/kernel/hung_task_timeout_secs " "to zero will disable this check. \n" "[One per CPU]" msgstr "" "大约每秒运行一次重置软锁的时间戳。如果它延迟超过60秒,那么就会打印一条信息。" "使用/proc/sys/kernel/hung_task_timeout_secs和/proc/sys/kernel/hung_task_ch来" "控制它的行为。 设置proc/sys/kernel/hung_task_timeout_secs为0可以关闭检查。" "【每个CPU一个】" #: tuna/help.py:27 msgid "" "This is the network transmit queue. Most of the time the network packets " "will be handled by the task that is sending the packets out, and doing so at " "the priority of that task. But if the protocol window or the network device " "queue is full, then the packets will be pushed off to later. The sirq-net-tx " "softirq is responsible for sending out these packets. \n" "[One per CPU]" msgstr "" "这是网络传输的队列。大多数的时间,网络中的包将被发送包的任务处理,并处理在那" "个任务的优先级里。但是如果协议窗口或者网络设备队列满了,那么这些包将被延迟处" "理。 sirq-net-tx软中断的责任就是发送这些包。【每个CPU一个】" #: tuna/help.py:28 msgid "" "Called after a completion to a block device is made. Looking further into " "this call, I only see a couple of users. The SCSI driver uses this as well " "as cciss. \n" "[One per CPU]" msgstr "" "当一个completion请求在块设备完成就会被调用。深入的看,我们只能看到SCSI驱动和cciss使用它。【每个CPU一个】" #: tuna/help.py:29 msgid "" "Catch all for those devices that couldn\"t use softirqs directly and mostly " "made before work queues were around. The difference between a tasklet and a " "softirq is that the same tasklet can not run on two different CPUs at the " "same time. In this regard it acts like a \"task\" (hence the name \"tasklet" "\"). Various devices use tasklets. \n" "[One per CPU]" msgstr "" "获取所有那些不能直接使用软中断和在工作队列出现之前就存在的设备。tasklet机制和" "软中断机制的区别在于同样的tasklet不能同时运行在两个不同的CPU上。在这点上,它" "像一个\"任务\"(因此叫\"tasklet\")。不同的设备都会使用tasklet。【每个CPU一个】" #: tuna/help.py:30 msgid "" "Per USB storage device virtual SCSI controller. Persistant across device " "insertion/removal, as is the SCSI node. This is done so that a device which " "is removed can be re-attached and be granted the same /dev node as before, " "creating persistance between connections of the target unit. Gets commands " "from the SCSI mid-layer and, after sanity checking several things, sends the " "command to the \"protocol\" handler. This handler is responsible for re-" "writing the command (if necessary) into a form which the device will accept. " "For example, ATAPI devices do not support 6-byte commands. Thus, they must " "be re-written into 10-byte variants." msgstr "" "每一个USB存储设备虚拟了SCSI的控制器。持续的insertion和removal就像SCSI的节点。" "这个的实现适应为一个被移除的设备可以重新贴附或者被认为是和以前的/dev节点一样," "在目的单元的连接中创建持续的连接。从SCSI的中间层获得命令,在检查完几个事物后," "将这些命令传送给\"protocol\"处理器。这个处理器的职责就是将这些命令(如" "果需要)重新写入设备接收的表中。比如,ATAPI设备不支持6字节的命令。因此他们必" "须被重写如10字节的变量里。" #: tuna/help.py:31 msgid "" "High priority system thread that performs thread migration by bumping thread " "off CPU then pushing onto another runqueue. \n" "[One per CPU]" msgstr "" "这是高优先级的系统线程,负责在线程迁移时将其他线程移离CPU,并推到另一个运行的" "队列中。【每个CPU一个】" #: tuna/help.py:32 msgid "" "Handles Sun RPC network messages (mainly for NFS) \n" "[One per CPU]" msgstr "处理Sun的RPC网络消息(主要是对NFS而言)。【每个CPU一个】" #: tuna/gui/util.py:50 msgid "Invalid affinity, specify a list of CPUs!" msgstr "无效的affinity,指定一个CPU列表。" #: tuna/gui/util.py:69 msgid "Invalid parameters!" msgstr "无效参数!" #: tuna/gui/util.py:76 #, python-format msgid "" "couldn't change pid %(pid)d from %(cpol)s(%(cpri)d) to %(npol)s(%(npri)d)!" msgstr "不能将pid %(pid)d从%(cpol)s(%(cpri)d)变成%(npol)s(%(npri)d)!" #: tuna/gui/util.py:110 #, python-format msgid "couldn't change pid %(pid)d from %(caff)s to %(naff)s!" msgstr "不能将pid %(pid)d从%(caff)s改为%(naff)s" #: tuna/gui/cpuview.py:15 #, python-format msgid "Couldn't change the affinity of %(tid)d to %(affinity)s!" msgstr "不能将%(tid)d的affinity改为%(affinity)s" #: tuna/gui/cpuview.py:46 #, python-format msgid "Socket %s" msgstr "套接字%s" #: tuna/gui/cpuview.py:64 msgid "Filter" msgstr "过滤器" #: tuna/gui/cpuview.py:68 msgid "CPU" msgstr "CPU" #: tuna/gui/cpuview.py:74 tuna/gui/cpuview.py:78 msgid "Usage" msgstr "利用率" #: tuna/gui/cpuview.py:197 msgid "I_nclude CPU" msgstr "包含CPU(_n)" #: tuna/gui/cpuview.py:198 msgid "_Isolate CPU" msgstr "隔离CPU(_I)" #: tuna/gui/cpuview.py:200 msgid "I_nclude CPU Socket" msgstr "包含CPU插槽(_n)" #: tuna/gui/cpuview.py:201 msgid "_Isolate CPU Socket" msgstr "隔离CPU插槽(_I)" #: tuna/gui/cpuview.py:202 msgid "_Restore CPU" msgstr "恢复CPU(_R)" #: tuna/gui/procview.py:200 tuna/gui/procview.py:220 tuna/gui/irqview.py:120 msgid "PID" msgstr "PID" #: tuna/gui/procview.py:201 tuna/gui/procview.py:221 tuna/gui/irqview.py:121 msgid "Policy" msgstr "调度策略" #: tuna/gui/procview.py:202 tuna/gui/procview.py:222 tuna/gui/irqview.py:122 msgid "Priority" msgstr "优先级" #: tuna/gui/procview.py:203 tuna/gui/procview.py:223 tuna/gui/irqview.py:123 #: tuna/gui/irqview.py:142 msgid "Affinity" msgstr "Affinity" #: tuna/gui/procview.py:204 msgid "VolCtxtSwitch" msgstr "VolCtxtSwitch" #: tuna/gui/procview.py:205 msgid "NonVolCtxtSwitch" msgstr "NonVolCtxtSwitch" #: tuna/gui/procview.py:206 tuna/gui/procview.py:224 msgid "Command Line" msgstr "命令" #: tuna/gui/procview.py:284 msgid "Kernel Thread" msgstr "内核线程" #: tuna/gui/procview.py:470 msgid "Save As" msgstr "另存為" #: tuna/gui/procview.py:528 #, python-format msgid "Kernel thread tunings saved to %s!" msgstr "内核线程调整另存为%s" #: tuna/gui/procview.py:541 msgid "_Set process attributes" msgstr "设置进程属性(_S)" #: tuna/gui/procview.py:543 msgid "Sto_p refreshing the process list" msgstr "停止刷新进程列表(_p)" #: tuna/gui/procview.py:545 msgid "_Refresh the process list" msgstr "刷新进程列表(_R)" #: tuna/gui/procview.py:548 msgid "_Hide kernel threads" msgstr "隐藏内核线程(_H)" #: tuna/gui/procview.py:550 msgid "_Show kernel threads" msgstr "显示内核线程(_S)" #: tuna/gui/procview.py:553 msgid "_Hide user threads" msgstr "隐藏用户线程(_H)" #: tuna/gui/procview.py:555 msgid "_Show user threads" msgstr "显示用户线程(_S)" #: tuna/gui/procview.py:557 msgid "_What is this?" msgstr "这是什么?(_W)" #: tuna/gui/procview.py:559 #, fuzzy msgid "_Save kthreads tunings" msgstr "保存内核调整(_S)" #: tuna/gui/irqview.py:119 tuna/gui/irqview.py:141 msgid "IRQ" msgstr "中断" #: tuna/gui/irqview.py:124 tuna/gui/irqview.py:143 msgid "Events" msgstr "事件" #: tuna/gui/irqview.py:125 tuna/gui/irqview.py:144 msgid "Users" msgstr "使用者" #: tuna/gui/irqview.py:289 msgid "_Set IRQ attributes" msgstr "设置中断属性(_S)" #: tuna/gui/irqview.py:291 msgid "Sto_p refreshing the IRQ list" msgstr "停止刷新中断列表(_p)" #: tuna/gui/irqview.py:293 msgid "_Refresh the IRQ list" msgstr "刷新中断列表(_R)" #: tuna-cmd.py:43 msgid "Usage: tuna [OPTIONS]" msgstr "用法:tuna [选项]" #: tuna-cmd.py:45 msgid "Give this help list" msgstr "给出帮助列表" #: tuna-cmd.py:46 msgid "Start the GUI" msgstr "启动图形界面" #: tuna-cmd.py:47 tuna-cmd.py:48 tuna-cmd.py:52 tuna-cmd.py:54 tuna-cmd.py:57 #: tuna-cmd.py:79 msgid "CPU-LIST" msgstr "CPU列表" #: tuna-cmd.py:47 #, python-format msgid "%(cpulist)s affected by commands" msgstr "命令影响 %(cpulist)s" #: tuna-cmd.py:49 msgid "Operation will affect children threads" msgstr "操作将会影响子线程" #: tuna-cmd.py:50 msgid "Display filter the selected entities" msgstr "显示过滤后的选中实体" #: tuna-cmd.py:51 #, python-format msgid "Move all threads away from %(cpulist)s" msgstr "从 %(cpulist)s 移出所有线程" #: tuna-cmd.py:53 #, python-format msgid "Allow all threads to run on %(cpulist)s" msgstr "允许所有线程运行在 %(cpulist)s 上" #: tuna-cmd.py:55 msgid "Operations will not affect kernel threads" msgstr "操作将不会影响内核线程" #: tuna-cmd.py:56 #, python-format msgid "Move selected entities to %(cpulist)s" msgstr "移动所有选中实体到 %(cpulist)s" #: tuna-cmd.py:59 msgid "Show network sockets in use by threads" msgstr "显示被线程使用的网络套接字" #: tuna-cmd.py:61 tuna-cmd.py:63 msgid "POLICY" msgstr "调度策略" #: tuna-cmd.py:62 tuna-cmd.py:63 msgid "RTPRIO" msgstr "实时进程" #: tuna-cmd.py:62 #, python-format msgid "Set thread scheduler tunables: %(policy)s and %(rtprio)s" msgstr "设置线程调度器的调整:%(policy)s 和 %(rtprio)s" #: tuna-cmd.py:64 msgid "Show thread list" msgstr "显示线程列表" #: tuna-cmd.py:65 tuna-cmd.py:66 msgid "IRQ-LIST" msgstr "中断列表" #: tuna-cmd.py:65 #, python-format msgid "%(irqlist)s affected by commands" msgstr "命令影响 %(irqlist)s" #: tuna-cmd.py:67 tuna-cmd.py:68 msgid "FILENAME" msgstr "文件名" #: tuna-cmd.py:67 #, python-format msgid "Save kthreads sched tunables to %(filename)s" msgstr "保存内核线程调度参数到 %(filename)s" #: tuna-cmd.py:70 tuna-cmd.py:71 msgid "CPU-SOCKET-LIST" msgstr "CPU套接字列表" #: tuna-cmd.py:70 #, python-format msgid "%(cpusocketlist)s affected by commands" msgstr "命令影响 %(cpusocketlist)s" #: tuna-cmd.py:73 tuna-cmd.py:74 msgid "THREAD-LIST" msgstr "线程列表" #: tuna-cmd.py:73 #, python-format msgid "%(threadlist)s affected by commands" msgstr "命令影响 %(threadlist)s " #: tuna-cmd.py:75 msgid "Operations will not affect user threads" msgstr "操作不会影响用户线程" #: tuna-cmd.py:76 msgid "Show version" msgstr "显示版本号" #: tuna-cmd.py:77 msgid "Provides help about selected entities" msgstr "提供有关选中实体的帮助" #: tuna-cmd.py:78 #, python-format msgid "Spread selected entities over %(cpulist)s" msgstr "扩展 %(cpulist)s 上被选中实体" #: tuna-cmd.py:94 #, python-format msgid "thread %d doesn't exists!" msgstr "线程 %d 不存在!" #: tuna-cmd.py:113 msgid "thread" msgstr "线程" #: tuna-cmd.py:405 tuna-cmd.py:410 tuna-cmd.py:427 msgid "requires a cpu list!" msgstr "需要一个CPU列表!" #: tuna-cmd.py:430 msgid "requires a list of threads/irqs!" msgstr "需要一个线程/中断列表!" #: tuna-cmd.py:456 #, python-format msgid "invalid socket %(socket)s sockets available: %(available)s" msgstr "无效的套接字 %(socket)s 可用套接字:%(available)s" #: tuna-cmd.py:481 msgid "requires a thread list!" msgstr "需要一个线程列表!" tuna-0.19/rpm/000077500000000000000000000000001437350234000132115ustar00rootroot00000000000000tuna-0.19/rpm/SPECS/000077500000000000000000000000001437350234000140665ustar00rootroot00000000000000tuna-0.19/rpm/SPECS/tuna.spec000066400000000000000000000111221437350234000157060ustar00rootroot00000000000000Name: tuna Version: 0.15 Release: 1%{?dist} License: GPLv2 Summary: Application tuning GUI & command line utility Group: Applications/System Source: http://userweb.kernel.org/~acme/tuna/%{name}-%{version}.tar.bz2 URL: http://userweb.kernel.org/~acme/tuna/ BuildArch: noarch BuildRequires: python-devel, gettext, desktop-file-utils Requires: python-ethtool Requires: python-linux-procfs >= 0.6 # This really should be a Suggests... # Requires: python-inet_diag BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %description Provides interface for changing scheduler and IRQ tunables, at whole CPU and at per thread/IRQ level. Allows isolating CPUs for use by a specific application and moving threads and interrupts to a CPU by just dragging and dropping them. Operations can be done on CPU sockets, understanding CPU topology. Can be used as a command line utility without requiring the GUI libraries to be installed. %package -n oscilloscope Summary: Generic graphical signal plotting tool Group: Applications/System Requires: python-matplotlib Requires: numpy Requires: pygtk2 Requires: tuna = %{version}-%{release} %description -n oscilloscope Plots stream of values read from standard input on the screen together with statistics and a histogram. Allows to instantly see how a signal generator, such as cyclictest, signaltest or even ping, reacts when, for instance, its scheduling policy or real time priority is changed, be it using tuna or plain chrt & taskset. %prep %setup -q %build %{python3} setup.py build %install rm -rf %{buildroot} %{python3} setup.py install --skip-build --root %{buildroot} mkdir -p %{buildroot}/%{_sysconfdir}/tuna/ mkdir -p %{buildroot}/{%{_bindir},%{_datadir}/tuna/help/kthreads,%{_mandir}/man8} mkdir -p %{buildroot}/%{_datadir}/polkit-1/actions/ install -p -m644 tuna/tuna_gui.glade %{buildroot}/%{_datadir}/tuna/ install -p -m755 tuna-cmd.py %{buildroot}/%{_bindir}/tuna install -p -m755 oscilloscope-cmd.py %{buildroot}/%{_bindir}/oscilloscope install -p -m644 help/kthreads/* %{buildroot}/%{_datadir}/tuna/help/kthreads/ install -p -m644 docs/tuna.8 %{buildroot}/%{_mandir}/man8/ install -p -m644 etc/tuna/example.conf %{buildroot}/%{_sysconfdir}/tuna/ install -p -m644 etc/tuna.conf %{buildroot}/%{_sysconfdir}/ install -p -m644 org.tuna.policy %{buildroot}/%{_datadir}/polkit-1/actions/ desktop-file-install --dir=%{buildroot}/%{_datadir}/applications tuna.desktop # l10n-ed message catalogues for lng in `cat po/LINGUAS`; do po=po/"$lng.po" mkdir -p %{buildroot}/%{_datadir}/locale/${lng}/LC_MESSAGES msgfmt $po -o %{buildroot}/%{_datadir}/locale/${lng}/LC_MESSAGES/%{name}.mo done %find_lang %name %clean rm -rf %{buildroot} %files -f %{name}.lang %defattr(-,root,root,-) %doc ChangeLog %if "%{python_ver}" >= "2.5" %{python2_sitelib}/*.egg-info %endif %{_bindir}/tuna %{_datadir}/tuna/ %{python3_sitelib}/tuna/ %{_mandir}/man8/tuna.8* %{_sysconfdir}/tuna.conf %{_sysconfdir}/tuna/* %{_datadir}/polkit-1/actions/org.tuna.policy %{_datadir}/applications/tuna.desktop %files -n oscilloscope %defattr(-,root,root,-) %{_bindir}/oscilloscope %doc docs/oscilloscope+tuna.html %doc docs/oscilloscope+tuna.pdf %changelog * Fri Feb 1 2013 Arnaldo Carvalho de Melo - 0.10.4-1 - New upstream release * Fri Aug 24 2012 Arnaldo Carvalho de Melo - 0.10.3-1 - New upstream release * Thu Jul 28 2011 Arnaldo Carvalho de Melo - 0.10.2-1 - New upstream release * Wed Feb 23 2011 Arnaldo Carvalho de Melo - 0.10.1-1 - New upstream release * Wed Feb 23 2011 Arnaldo Carvalho de Melo - 0.10-1 - New upstream release * Mon May 17 2010 Arnaldo Carvalho de Melo - 0.9.3-1 - New upstream release - Fixes the folowing bugzilla.redhat.com tickets: - 563355 error in tuna --help output - 574950 cannot use cpu ranges in the tuna GUI - 559770 tuna backtrace when moving threads - 563352 tuna backtrace when no thread list is given for --priority - 563350 tuna backtrace when scheduler is mis-typed. * Thu Nov 12 2009 Arnaldo Carvalho de Melo - 0.9.2-1 - New upstream release * Thu Sep 03 2009 Arnaldo Carvalho de Melo - 0.9.1-1 - New upstream release * Wed Aug 26 2009 Arnaldo Carvalho de Melo - 0.9-3 - Rewrite the oscilloscope package summary - Remove the shebang in tuna/oscilloscope.py * Mon Aug 17 2009 Arnaldo Carvalho de Melo - 0.9-2 - Use install -p - Add BuildRequires for gettext * Fri Jul 10 2009 Arnaldo Carvalho de Melo - 0.9-1 - Fedora package reviewing changes: introduce ChangeLog file tuna-0.19/setup.py000077500000000000000000000016561437350234000141400ustar00rootroot00000000000000#!/usr/bin/python3 import os import sysconfig from os.path import isfile, relpath from setuptools import setup if isfile("MANIFEST"): os.unlink("MANIFEST") SCHEME = 'rpm_prefix' if SCHEME not in sysconfig.get_scheme_names(): SCHEME = 'posix_prefix' # Get PYTHONLIB with no prefix so --prefix installs work. PYTHONLIB = relpath(sysconfig.get_path('platlib', SCHEME), '/usr') setup(name="tuna", version = "0.19", description = "Application tuning GUI", author = "Arnaldo Carvalho de Melo", author_email = "acme@redhat.com", url = "http://userweb.kernel.org/tuna", license = "GPLv2", long_description = """\ Provides interface for changing scheduler and IRQ tunables, at whole CPU and at per thread/IRQ level. Allows isolating CPUs for use by a specific application and moving threads and interrupts to a CPU by just dragging and dropping them. """, packages = ["tuna", "tuna/gui"], ) tuna-0.19/testuna000077500000000000000000000130571437350234000140320ustar00rootroot00000000000000#!/bin/bash # Regression tests for tuna # (c) 2008 Red Hat Inc. # Arnaldo Carvalho de Melo # Released under the GPLv2 dprint() { [ -n "$VERBOSE" ] && echo $1 } ktpidof() { echo $(ps ax -To pid,cmd | grep "\[$1.*\]" | head -1 | cut -d'[' -f 1) } get_rtprio() { echo $(chrt -p $1 | grep priority | cut -d ':' -f 2) } get_policy() { echo $(chrt -p $1 | grep policy | cut -d ':' -f 2) } get_affinity() { echo $(taskset -p $1 | grep 'current affinity' | cut -d ':' -f 2) } get_nr_processors() { echo $(grep -i "^processor.*: " /proc/cpuinfo | tail -1 | cut -d ':' -f 2) } get_nr_cpu_sockets() { echo $(grep "^physical id" /proc/cpuinfo | cut -d: -f2 | sort -u | wc -l) } die() { [ -z "$VERBOSE" ] && echo -n "$2: " echo $1 rtctl --file $INITIAL reset taskset -p $INITIAL_INIT_AFFINITY 1 > /dev/null rm -rf $TESTUNA_DIR exit 1 } die_with_a_diff() { [ -n "$VERBOSE" ] && diff -u $INITIAL $NEW die "$1" } tuna_save() { tuna --save $TEMPCONF grep -v '^# rtctl --file ' $TEMPCONF > $1 rm -f $TEMPCONF } die_if_not_saved() { dprint "$2" tuna_save $NEW (diff -u $INITIAL $NEW | diffstat | \ grep -q "[ \t]*1 file changed, $1 insertions*(+), $1 deletions*(-)") || die_with_a_diff 'FAILED!' "$2" } die_if_conf_changed() { dprint "$1" tuna_save $NEW diff -qu $INITIAL $NEW > /dev/null || die_with_a_diff 'FAILED!' "$1": } die_if_zero() { dprint "$2" [ $1 -eq 0 ] && die 'FAILED!' "$2" } die_if_not_zero() { dprint "$2" [ $1 -ne 0 ] && die 'FAILED!' "$2" } die_if_not_equal() { dprint "$3" [ $1 -ne $2 ] && die 'FAILED!' "$3" } die_if_not_str_equal() { dprint "$3" [ $1 != $2 ] && die 'FAILED!' "$3" } TESTUNA_DIR=$(mktemp -d -t testuna.XXXXXX) || exit 1 INITIAL=$TESTUNA_DIR/initial.tuna INITIAL_INIT_AFFINITY=$(get_affinity 1) TEMPCONF=$TESTUNA_DIR/tempnew.tuna NEW=$TESTUNA_DIR/new.tuna [ $# -eq 1 ] && VERBOSE=1 dprint "Saving initial configuration" tuna_save $INITIAL TUNA_RPM_VERSION=$(rpm -q --qf "%{version}\n" tuna) TUNA_BIN_VERSION=$(tuna --version) die_if_not_str_equal "$TUNA_RPM_VERSION" "$TUNA_BIN_VERSION" \ "Verifying --version ($TUNA_BIN_VERSION) matches package version ($TUNA_RPM_VERSION)" rtctl --file $INITIAL reset die_if_conf_changed "Replaying initial config" PID=$(ktpidof "watchdog") RTPRIO=$(get_rtprio $PID) POLICY=$(get_policy $PID) POLICY=$(echo ${POLICY:6:1} | tr 'A-Z' 'a-z') chrt -$POLICY -p $((RTPRIO - 1)) $PID die_if_not_saved 1 'Saving changes to a kernel thread priority' chrt -$POLICY -p $RTPRIO $PID die_if_conf_changed 'Restoring kernel thread priority' new_policy=$(echo $POLICY | tr fr rf) chrt -$new_policy -p $RTPRIO $PID die_if_not_saved 1 'Changing kernel thread sched policy' chrt -$POLICY -p $RTPRIO $PID die_if_conf_changed 'Restoring kernel thread sched policy' PID=$(ktpidof "kthreadd") AFFINITY=$(get_affinity $PID) taskset -p 0x2 $PID > /dev/null die_if_not_saved 1 'Changing kernel thread SMP affinity mask' taskset -p $AFFINITY $PID > /dev/null die_if_conf_changed 'Restoring kernel thread SMP affinity mask' NR_PROCESSORS=$(get_nr_processors) for PROCESSOR in $(seq 0 $NR_PROCESSORS) ; do taskset -p 0xff 1 > /dev/null PROCESSOR_AFFINITY=$(printf "%#x\n" $((1 << PROCESSOR))) tuna --cpu $PROCESSOR --isolate AFFINITY=0x$(get_affinity 1) die_if_not_zero $((AFFINITY & PROCESSOR_AFFINITY)) "Isolating CPU $PROCESSOR" tuna --cpu $PROCESSOR --include AFFINITY=0x$(get_affinity 1) die_if_zero $((AFFINITY & PROCESSOR_AFFINITY)) "Including CPU $PROCESSOR" done NEW_AFFINITY=$((1 << NR_PROCESSORS | 1)) if [ $NR_PROCESSORS -gt 2 ]; then tuna --cpu=0,$NR_PROCESSORS --isolate for PID in $(cd /proc; ls -d [0-9]*) ; do [ -n "$(cat /proc/$PID/cmdline 2> /dev/null)" ] || continue AFFINITY=0x$(get_affinity $PID) || continue die_if_not_zero $((AFFINITY & NEW_AFFINITY)) \ "Verifying isolation of first and last processor for PID $PID" done fi tuna --cpu=0,$NR_PROCESSORS --include AFFINITY=0x$(get_affinity 1) die_if_not_equal $((AFFINITY & NEW_AFFINITY)) $NEW_AFFINITY "Including first and last processor" taskset -p 0xff 1 > /dev/null NEW_AFFINITY=$((1 << NR_PROCESSORS | 1)) tuna --cpu=0,$NR_PROCESSORS --thread 1 --move AFFINITY=0x$(get_affinity 1) die_if_not_equal $((AFFINITY & NEW_AFFINITY)) $NEW_AFFINITY "Moving init to just first and last processor" NR_CPU_SOCKETS=$(get_nr_cpu_sockets) if [ $NR_CPU_SOCKETS -ge 2 ]; then CPU1_SIBLINGS=$(printf "%d" 0x$(cat /sys/devices/system/cpu/cpu1/topology/core_siblings | cut -d',' -f2)) CPU1_SOCKET=$(cat /sys/devices/system/cpu/cpu1/topology/physical_package_id) tuna --sockets=$CPU1_SOCKET --isolate AFFINITY=0x$(get_affinity 1) die_if_not_zero $((AFFINITY & CPU1_SIBLINGS)) \ "Verifying isolation of socket $CPU1_SOCKET" tuna --sockets=$CPU1_SOCKET --include AFFINITY=0x$(get_affinity 1) die_if_not_equal $((AFFINITY & CPU1_SIBLINGS)) $CPU1_SIBLINGS \ "Verifying inclusion of socket $CPU1_SOCKET" tuna --sockets=$CPU1_SOCKET --isolate tuna --sockets=$CPU1_SOCKET --thread 1 --move AFFINITY=0x$(get_affinity 1) die_if_not_equal $((AFFINITY & CPU1_SIBLINGS)) $CPU1_SIBLINGS "Moving init to CPU socket $CPU1_SOCKET" fi if [ $NR_PROCESSORS -gt 2 ]; then THREAD_PREFIX="watchdog/" PID=$(ps h -C ${THREAD_PREFIX}0 -o pid) RTPRIO=$(get_rtprio $PID) NEW_RTPRIO=$((RTPRIO - 1)) tuna -t $THREAD_PREFIX* -p $NEW_RTPRIO for CPU in $(seq 0 $((NR_PROCESSORS - 1))); do PID=$(ps h -C ${THREAD_PREFIX}$CPU -o pid) RTPRIO=$(get_rtprio $PID) die_if_not_equal $RTPRIO $NEW_RTPRIO "Using --thread globbing" done fi taskset -p $INITIAL_INIT_AFFINITY 1 > /dev/null rtctl --file $INITIAL reset echo 'PASS: Healthy tuna, no lead found, eat!' exit 0 tuna-0.19/tuna-cmd.py000077500000000000000000000710131437350234000145020ustar00rootroot00000000000000#! /usr/bin/python3 # -*- python -*- # -*- coding: utf-8 -*- # tuna - Application Tuning GUI # Copyright (C) 2008, 2009, 2010, 2011 Red Hat Inc. # Arnaldo Carvalho de Melo # # This application is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2. # # This application is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. """ tuna - Application Tuning Program""" import argparse import os import sys import errno import re import fnmatch import gettext import locale from functools import reduce import tuna.new_eth as ethtool import tuna.tuna_sched as tuna_sched import procfs from tuna import tuna, sysfs import logging import time import shutil def get_loglevel(level): if level.isdigit() and int(level) in range(0,5): # logging built-in module levels: # 0 - NOTSET # 10 - DEBUG # 20 - INFO # 30 - WARNING # 40 - ERROR return int(level) * 10 level_str = level.upper() if level_str == "CRITICAL": raise ValueError("CRITICAL level is not supported by tuna") return level_str def setup_logging(logger_name): logger = logging.getLogger(logger_name) logger.setLevel(logging.DEBUG) logger.propagate = False return logger def add_handler(loglevel, tofile=False): formatter = logging.Formatter('[%(levelname)s] %(message)s') if tofile: lognum = 1 date = time.strftime("%Y%m%d") while os.path.exists("tuna-{}-{}".format(date, lognum)): lognum += 1 path = "tuna-{}-{}/log/Log".format(date,lognum) os.makedirs(os.path.dirname(path), exist_ok=True, mode=0o777) handler = logging.FileHandler(path) else: handler = logging.StreamHandler(sys.stderr) handler.setFormatter(formatter) handler.setLevel(loglevel) return handler try: import inet_diag have_inet_diag = True except: have_inet_diag = False # FIXME: ETOOMANYGLOBALS, we need a class! nr_cpus = None ps = None irqs = None class HelpMessageParser(argparse.ArgumentParser): def error(self, message): sys.stderr.write(f'error: {message}\n') self.print_help() sys.exit(2) def gen_parser(): POS = { "cpu_list": dict(metavar='CPU-LIST', type=tuna.cpustring_to_list, help="CPU-LIST affected by commands"), "thread_list": dict(metavar='THREAD-LIST', type=threadstring_to_list, help="THREAD-LIST affected by commands"), "filename": dict(metavar='FILENAME', type=str, help="Save kthreads sched tunables to this file"), "profilename": dict(type=str, help="Apply changes described in this file"), "run_command": dict(metavar='COMMAND', type=str, help="fork a new process and run the \"COMMAND\""), "priority": dict(type=tuna.get_policy_and_rtprio, metavar="POLICY:RTPRIO", help="Set thread scheduler tunables: POLICY and RTPRIO"), } MODS = { "logging": dict(dest='loglevel', metavar='LOG-LEVEL', type=get_loglevel, help="Log application details to file for given LOG-LEVEL"), "debug" : dict(action='store_true', dest='debug', help='Print DEBUG level logging details to console'), "version": dict(action='version', version='0.19', help="show version"), "threads": dict(dest='thread_list', default=[], metavar='THREAD-LIST', type=threadstring_to_list, help="THREAD-LIST affected by commands"), "irqs": dict(dest='irq_list', default=[], metavar='IRQ-LIST', type=irqstring_to_list, help="IRQ-LIST affect by commands"), "cpus": dict(dest='cpu_list', default=[], metavar='CPU-LIST', type=tuna.cpustring_to_list, help='CPU-LIST affected by commands'), "sockets": dict(dest='cpu_list', default=[], metavar='CPU-SOCKET-LIST', type=socketstring_to_list, help="CPU-SOCKET-LIST affected by commands"), "show_sockets": dict(action='store_true', help='Show network sockets in use by threads'), "cgroups": dict(action='store_true', dest='cgroups', help='Display the processes with the type of cgroups they are in'), "spaced": dict(action='store_false', dest='compact', help='Display spaced view for cgroups'), "affect_children": dict(action='store_true', help="Operation will affect children threads"), "nohz_full": dict(action='store_true', help="CPUs in nohz_full kernel command line will be affected by operations"), "no_uthreads": dict(action='store_false', dest='uthreads', help="Operations will not affect user threads"), "no_kthreads": dict(action='store_false', dest='kthreads', help="Operations will not affect kernel threads"), "disable_perf": dict(action='store_true', help="Explicitly disable usage of perf in GUI for process view"), "refresh": dict(default=2500, metavar='MSEC', type=int, help="Refresh the GUI every MSEC milliseconds"), "priority": dict(default=(None, None), metavar="POLICY:RTPRIO", type=tuna.get_policy_and_rtprio, help="Set thread scheduler tunables: POLICY and RTPRIO"), "background": dict(action='store_true', help="Run command as background task") } parser = HelpMessageParser(description="tuna - Application Tuning Program") parser._positionals.title = "commands" parser.add_argument('-v', '--version', **MODS['version']) parser.add_argument('-L', '--logging', **MODS['logging']) parser.add_argument('-D', '--debug', **MODS['debug']) subparser = parser.add_subparsers(dest='command') isolate = subparser.add_parser('isolate', description="Move all allowed threads and IRQs away from CPU-LIST", help="Move all allowed threads and IRQs away from CPU-LIST") include = subparser.add_parser('include', description="Allow all threads to run on CPU-LIST", help="Allow all threads to run on CPU-LIST") move = subparser.add_parser('move', description="Move selected entities to CPU-LIST", help="Move selected entities to CPU-LIST") spread = subparser.add_parser('spread', description="Spread selected entities to CPU-LIST", help="Spread selected entities over CPU-LIST") priority = subparser.add_parser('priority', description="Set thread scheduler tunables: POLICY and RTPRIO", help="Set thread scheduler tunables: POLICY and RTPRIO") run = subparser.add_parser('run', description="Fork a new process and run the COMMAND", help="Fork a new process and run the COMMAND") save = subparser.add_parser('save', description="Save kthreads sched tunables to FILENAME", help="Save kthreads sched tunables to FILENAME") apply = subparser.add_parser('apply', description="Apply changes described in profile", help="Apply changes described in profile") show_threads = subparser.add_parser('show_threads', description='Show thread list', help='Show thread list') show_irqs = subparser.add_parser('show_irqs', description='Show IRQ list', help='Show IRQ list') show_configs = subparser.add_parser('show_configs', description='List preloaded profiles', help='List preloaded profiles') what_is = subparser.add_parser('what_is', description='Provides help about selected entities', help='Provides help about selected entities') gui = subparser.add_parser('gui', description="Start the GUI", help="Start the GUI") isolate_group = isolate.add_mutually_exclusive_group(required=True) isolate_group.add_argument('-c', '--cpus', **MODS['cpus']) isolate_group.add_argument('-S', '--sockets', **MODS['sockets']) isolate_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) include_group = include.add_mutually_exclusive_group(required=True) include_group.add_argument('-c', '--cpus', **MODS['cpus']) include_group.add_argument('-S', '--sockets', **MODS['sockets']) include_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) move_group = move.add_mutually_exclusive_group(required=True) move_group.add_argument('-c', '--cpus', **MODS['cpus']) move_group.add_argument('-S', '--sockets', **MODS['sockets']) move_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) move.add_argument('-t', '--threads', **MODS['threads']) move.add_argument('-q', '--irqs', **MODS['irqs']) spread_group = spread.add_mutually_exclusive_group(required=True) spread_group.add_argument('-c', '--cpus', **MODS['cpus']) spread_group.add_argument('-S', '--sockets', **MODS['sockets']) spread_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) spread.add_argument('-t', '--threads', **MODS['threads']) spread.add_argument('-q', '--irqs', **MODS['irqs']) priority.add_argument('priority', **POS['priority']) priority.add_argument('-t', '--threads', **MODS['threads'], required=True) priority.add_argument('-C', '--affect_children', **MODS['affect_children']) run.add_argument('run_command', **POS['run_command']) run_group = run.add_mutually_exclusive_group(required=False) run_group.add_argument('-c', '--cpus', **MODS['cpus']) run_group.add_argument('-S', '--sockets', **MODS['sockets']) run_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) run.add_argument('-p', '--priority', **MODS['priority']) run.add_argument('-b', '--background', **MODS['background']) save.add_argument('filename', **POS['filename']) save_group = save.add_mutually_exclusive_group(required=False) save_group.add_argument('-c', '--cpus', **MODS['cpus']) save_group.add_argument('-S', '--sockets', **MODS['sockets']) save_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) save.add_argument('-t', '--threads', **MODS['threads']) apply.add_argument('profilename', **POS['profilename']) show_threads_group1 = show_threads.add_mutually_exclusive_group(required=False) show_threads_group1.add_argument('-c', '--cpus', **MODS['cpus']) show_threads_group1.add_argument('-N', '--nohz_full', **MODS['nohz_full']) show_threads_group1.add_argument('-S', '--sockets', **MODS['sockets']) show_threads_group2 = show_threads.add_mutually_exclusive_group(required=False) show_threads_group2.add_argument('-t', '--threads', **MODS['threads']) show_threads_group2.add_argument('-q', '--irqs', **MODS['irqs']) show_threads.add_argument('-U', '--no_uthreads', **MODS['no_uthreads']) show_threads.add_argument('-K', '--no_kthreads', **MODS['no_kthreads']) show_threads.add_argument('-C', '--affect_children', **MODS['affect_children']) if have_inet_diag: show_threads.add_argument('-n', '--show_sockets', **MODS['show_sockets']) show_threads.add_argument('-G', '--cgroups', **MODS['cgroups']) show_threads.add_argument('-z', '--spaced', **MODS['spaced']) show_irqs_group = show_irqs.add_mutually_exclusive_group(required=False) show_irqs_group.add_argument('-c', '--cpus', **MODS['cpus']) show_irqs_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) show_irqs_group.add_argument('-S', '--sockets', **MODS['sockets']) show_irqs.add_argument('-q', '--irqs', **MODS['irqs']) what_is.add_argument('thread_list', **POS['thread_list']) gui.add_argument('-d', '--disable_perf', **MODS['disable_perf']) gui.add_argument('-R', '--refresh', **MODS['refresh']) gui_group = gui.add_mutually_exclusive_group(required=False) gui_group.add_argument('-c', '--cpus', **MODS['cpus']) gui_group.add_argument('-N', '--nohz_full', **MODS['nohz_full']) gui_group.add_argument('-S', '--sockets', **MODS['sockets']) gui.add_argument('-U', '--no_uthreads', **MODS['no_uthreads']) gui.add_argument('-K', '--no_kthreads', **MODS['no_kthreads']) return parser def get_nr_cpus(): """ Get all cpus including disabled cpus """ global nr_cpus if nr_cpus: return nr_cpus nr_cpus = os.sysconf('SC_NPROCESSORS_CONF') return nr_cpus nics = None def get_nics(): global nics if nics: return nics nics = ethtool.get_active_devices() return nics def thread_help(tid): global ps if not ps: ps = procfs.pidstats() if tid not in ps: print(f"tuna: thread {tid} doesn't exist!") return pinfo = ps[tid] cmdline = procfs.process_cmdline(pinfo) help, title = tuna.kthread_help_plain_text(tid, cmdline) print(title, "\n\n") if help.isspace(): help = "No help description available." print(help) def save(cpu_list, thread_list, filename): kthreads = tuna.get_kthread_sched_tunings() for name in list(kthreads.keys()): kt = kthreads[name] if (cpu_list and not set(kt.affinity).intersection(set(cpu_list))) or \ (thread_list and kt.pid not in thread_list): del kthreads[name] tuna.generate_rtgroups(filename, kthreads, get_nr_cpus()) def ps_show_header(has_ctxt_switch_info, cgroups=False): print("%7s %6s %5s %7s %s" % (" ", " ", " ", _("thread"), has_ctxt_switch_info and "ctxt_switches" or "")) print("%7s %6s %5s %7s%s %15s" % ("pid", "SCHED_", "rtpri", "affinity", has_ctxt_switch_info and " %9s %12s" % ( "voluntary", "nonvoluntary") or "", "cmd"), end=' ') print(" %7s" % ("cgroup") if cgroups else "") def ps_show_sockets(pid, ps, inodes, inode_re, indent=0): header_printed = False dirname = f"/proc/{pid}/fd" try: filenames = os.listdir(dirname) except: # Process died return sindent = " " * indent for filename in filenames: pathname = os.path.join(dirname, filename) try: linkto = os.readlink(pathname) except: # Process died continue inode_match = inode_re.match(linkto) if not inode_match: continue inode = int(inode_match.group(1)) if inode not in inodes: continue if not header_printed: print("%s%-10s %-6s %-6s %15s:%-5s %15s:%-5s" % (sindent, "State", "Recv-Q", "Send-Q", "Local Address", "Port", "Peer Address", "Port")) header_printed = True s = inodes[inode] print("%s%-10s %-6d %-6d %15s:%-5d %15s:%-5d" % (sindent, s.state(), s.receive_queue(), s.write_queue(), s.saddr(), s.sport(), s.daddr(), s.dport())) def format_affinity(affinity): if len(affinity) <= 4: return ",".join(str(a) for a in affinity) return ",".join(str(hex(a)) for a in procfs.hexbitmask(affinity, get_nr_cpus())) def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, columns=None, compact=True): global irqs try: affinity = format_affinity(os.sched_getaffinity(pid)) except OSError as e: if e.args[0] == errno.ESRCH: return raise e sched = tuna_sched.sched_str(os.sched_getscheduler(pid))[6:] rtprio = int(ps[pid]["stat"]["rt_priority"]) cgout = ps[pid]["cgroups"] cmd = ps[pid]["stat"]["comm"] users = "" if tuna.is_irq_thread(cmd): try: if not irqs: irqs = procfs.interrupts() users = irqs[tuna.irq_thread_number(cmd)]["users"] for u in users: if u in get_nics(): users[users.index(u)] = "%s(%s)" % ( u, ethtool.get_module(u)) users = ",".join(users) except: users = "Not found in /proc/interrupts!" ctxt_switch_info = "" if has_ctxt_switch_info: voluntary_ctxt_switches = int( ps[pid]["status"]["voluntary_ctxt_switches"]) nonvoluntary_ctxt_switches = int( ps[pid]["status"]["nonvoluntary_ctxt_switches"]) ctxt_switch_info = " %9d %12s" % (voluntary_ctxt_switches, nonvoluntary_ctxt_switches) # Indent affected children s1 = " %-5d " % pid if affect_children else " %-5d" % pid print(s1, end=' ') s2 = "%6s %5d %8s%s %15s %s" % (sched, rtprio, affinity, ctxt_switch_info, cmd, users) print(s2, end=' ') if cgroups: length = int(columns) - len(s1 + " ") - len(s2 + " ") if len(" %9s" % cgout) <= length: print("%s" % cgout) else: print("\n %s" % cgout + ("" if compact else "\n")) else: print() if sock_inodes: ps_show_sockets(pid, ps, sock_inodes, sock_inode_re, affect_children and 3 or 4) if affect_children and "threads" in ps[pid]: for tid in list(ps[pid]["threads"].keys()): ps_show_thread(tid, False, ps[pid]["threads"], has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, columns, compact) def ps_show(ps, affect_children, thread_list, cpu_list, irq_list_numbers, show_uthreads, show_kthreads, has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact): ps_list = [] for pid in list(ps.keys()): iskth = tuna.iskthread(pid) if not show_uthreads and not iskth: continue if not show_kthreads and iskth: continue in_irq_list = False if irq_list_numbers: if tuna.is_hardirq_handler(ps, pid): try: irq = int(ps[pid]["stat"]["comm"][4:]) if irq not in irq_list_numbers: if not thread_list: continue else: in_irq_list = True except: pass elif not thread_list: continue if not in_irq_list and thread_list and pid not in thread_list: continue try: affinity = os.sched_getaffinity(pid) except OSError as e: if e.args[0] == errno.ESRCH: continue raise e if cpu_list and not set(cpu_list).intersection(set(affinity)): continue ps_list.append(pid) ps_list.sort() # Width of terminal in columns columns = 80 if cgroups: if os.isatty(sys.stdout.fileno()): columns = shutil.get_terminal_size().columns for pid in ps_list: ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, columns, compact) def load_socktype(socktype, inodes): idiag = inet_diag.create(socktype=socktype) while True: try: s = idiag.get() except: break inodes[s.inode()] = s def load_sockets(): inodes = {} for socktype in (inet_diag.TCPDIAG_GETSOCK, inet_diag.DCCPDIAG_GETSOCK): load_socktype(socktype, inodes) return inodes def do_ps(thread_list, cpu_list, irq_list, show_uthreads, show_kthreads, affect_children, show_sockets, cgroups, compact): ps = procfs.pidstats() if affect_children: ps.reload_threads() sock_inodes = None sock_inode_re = None if show_sockets: sock_inodes = load_sockets() sock_inode_re = re.compile(r"socket:\[(\d+)\]") has_ctxt_switch_info = "voluntary_ctxt_switches" in ps[1]["status"] try: if sys.stdout.isatty(): ps_show_header(has_ctxt_switch_info, cgroups) ps_show(ps, affect_children, thread_list, cpu_list, irq_list, show_uthreads, show_kthreads, has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact) except IOError: # 'tuna -P | head' for instance pass def find_drivers_by_users(users): nics = get_nics() drivers = [] for u in users: try: idx = u.index('-') u = u[:idx] except: pass if u in nics: driver = ethtool.get_module(u) if driver not in drivers: drivers.append(driver) return drivers def show_irqs(irq_list, cpu_list): global irqs if not irqs: irqs = procfs.interrupts() if sys.stdout.isatty(): print("%4s %-16s %8s" % ("#", _("users"), _("affinity"),)) sorted_irqs = [] for k in list(irqs.keys()): try: irqn = int(k) affinity = irqs[irqn]["affinity"] except: continue if irq_list and irqn not in irq_list: continue if cpu_list and not set(cpu_list).intersection(set(affinity)): continue sorted_irqs.append(irqn) sorted_irqs.sort() for irq in sorted_irqs: affinity = format_affinity(irqs[irq]["affinity"]) users = irqs[irq]["users"] print("%4d %-16s %8s" % (irq, ",".join(users), affinity), end=' ') drivers = find_drivers_by_users(users) if drivers: print(" %s" % ",".join(drivers)) else: print() def do_list_op(op, current_list, op_list): if not current_list: current_list = [] if op == '+': return list(set(current_list + op_list)) if op == '-': return list(set(current_list) - set(op_list)) return list(set(op_list)) def threadstring_to_list(threadstr): global ps thread_list = [] thread_strings = list(set(threadstr.split(','))) for s in thread_strings: if s.isdigit(): thread_list.append(int(s)) else: ps = procfs.pidstats() try: thread_list += ps.find_by_regex(re.compile(fnmatch.translate(s))) except: thread_list += ps.find_by_name(s) return thread_list def irqstring_to_list(irqstr): irq_list = [] irq_strings = list(set(irqstr.split(','))) for s in irq_strings: if s.isdigit(): irq_list.append(int(s)) else: # find_by_user_regex returns a list of strings corresponding to irq number irq_list_str = procfs.interrupts().find_by_user_regex(re.compile(fnmatch.translate(s))) irq_list += [int(i) for i in irq_list_str if i.isdigit()] return irq_list def socketstring_to_list(socketstr): cpu_list = [] socket_strings = list(set(socketstr.split(','))) cpu_info = sysfs.cpus() for s in socket_strings: if s not in cpu_info.sockets: print("tuna: invalid socket %(socket)s sockets available: %(available)s" % {"socket": s,"available": ",".join(list(cpu_info.sockets.keys()))}) sys.exit(2) cpu_list += [int(cpu.name[3:]) for cpu in cpu_info.sockets[s]] return cpu_list def pick_op(argument): if argument == "": return (None, argument) if argument[0] in ('+', '-'): return (argument[0], argument[1:]) return (None, argument) def i18n_init(): (app, localedir) = ('tuna', '/usr/share/locale') locale.setlocale(locale.LC_ALL, '') gettext.bindtextdomain(app, localedir) gettext.textdomain(app) gettext.install(app, localedir) def apply_config(filename): from tuna.config import Config config = Config() if os.path.exists(filename): config.config['root'] = os.getcwd() + "/" filename = os.path.basename(filename) else: if not os.path.exists(config.config['root']+filename): print(filename + _(" not found!")) sys.exit(1) if config.loadTuna(filename): sys.exit(1) ctrl = 0 values = {} values['toapply'] = {} for index in range(len(config.ctlParams)): for opt in config.ctlParams[index]: values['toapply'][ctrl] = {} values['toapply'][ctrl]['label'] = opt values['toapply'][ctrl]['value'] = config.ctlParams[index][opt] ctrl = ctrl + 1 config.applyChanges(values) def list_config(): from tuna.config import Config config = Config() print(_("Preloaded config files:")) for value in config.populate(): print(value) sys.exit(1) def nohz_full_to_cpu(): try: return tuna.nohz_full_list() except: print("tuna: --nohz_full " + _(" needs nohz_full=cpulist on the kernel command line")) sys.exit(2) def main(): global ps i18n_init() parser = gen_parser() # Set all necessary defaults for gui subparser if no arguments provided args = parser.parse_args() if len(sys.argv) > 1 else parser.parse_args(['gui']) if args.debug: my_logger = setup_logging("my_logger") my_logger.addHandler(add_handler("DEBUG", tofile=False)) my_logger.info("Debug option set") if args.loglevel: if not args.debug: my_logger = setup_logging("my_logger") try: my_logger.addHandler(add_handler(args.loglevel, tofile=True)) my_logger.info("Logging option set at log level {}".format(args.loglevel)) except ValueError as e: print(e, "tuna: --logging requires valid logging level\n") print("Valid log levels: NOTSET, DEBUG, INFO, WARNING, ERROR") print("Log levels may be specified numerically (0-4)\n") if 'irq_list' in vars(args): ps = procfs.pidstats() if tuna.has_threaded_irqs(ps): for irq in args.irq_list: irq_re = tuna.threaded_irq_re(irq) irq_threads = ps.find_by_regex(irq_re) if irq_threads: # Change the affinity of the thread too # as we can't rely on changing the irq # affinity changing the affinity of the # thread or vice versa. We need to change # both. if 'thread_list' in vars(args): args.thread_list += irq_threads if 'nohz_full' in vars(args) and args.nohz_full: args.cpu_list = nohz_full_to_cpu() if args.command in ['apply', 'a']: apply_config(args.profilename) elif args.command in ['include', 'I']: tuna.include_cpus(args.cpu_list, get_nr_cpus()) elif args.command in ['isolate', 'i']: tuna.isolate_cpus(args.cpu_list, get_nr_cpus()) elif args.command in ['run', 'r']: tuna.run_command(args.run_command, args.priority[0], args.priority[1], args.cpu_list, args.background) elif args.command in ['priority', 'p']: try: tuna.threads_set_priority(args.thread_list, args.priority, args.affect_children) except OSError as err: print(f"tuna: {err}") sys.exit(2) elif args.command in ['show_configs']: list_config() elif args.command in ['show_threads']: do_ps(args.thread_list, args.cpu_list, args.irq_list, args.uthreads, args.kthreads, args.affect_children, args.show_sockets if "show_sockets" in args else None, args.cgroups, args.compact) elif args.command in ['show_irqs']: show_irqs(args.irq_list, args.cpu_list) elif args.command in ['move', 'm', 'spread', 'x']: spread = args.command in ['spread', 'x'] if not (args.thread_list or args.irq_list): parser.error(f"tuna: {args.command} requires a thread/irq list!\n") if args.thread_list: tuna.move_threads_to_cpu(args.cpu_list, args.thread_list, spread=spread) if args.irq_list: tuna.move_irqs_to_cpu(args.cpu_list, args.irq_list, spread=spread) elif args.command in ['s', 'save']: save(args.cpu_list, args.thread_list, args.filename) elif args.command in ['W', 'what_is']: for tid in args.thread_list: thread_help(tid) elif args.command in ['g', 'gui']: try: from tuna import tuna_gui except ImportError: # gui packages not installed print(_('tuna: packages needed for the GUI missing.')) print(_(' Make sure xauth, pygtk2-libglade are installed.')) parser.print_help() return except RuntimeError: print("tuna: machine needs to be authorized via xhost or ssh -X?") return try: app = tuna_gui.main_gui(args.kthreads, args.uthreads, args.cpu_list, args.refresh, args.disable_perf) app.run() except KeyboardInterrupt: pass if __name__ == '__main__': main() tuna-0.19/tuna.desktop000066400000000000000000000003051437350234000147530ustar00rootroot00000000000000[Desktop Entry] Name=tuna GenericName=Application Tuner Exec=tuna --gui Type=Application Terminal=false Comment=Thread and IRQ attribute management Icon=gtk-preferences Categories=System;Settings; tuna-0.19/tuna/000077500000000000000000000000001437350234000133625ustar00rootroot00000000000000tuna-0.19/tuna/__init__.py000077500000000000000000000002361437350234000154770ustar00rootroot00000000000000""" Copyright (c) 2008, 2009 Red Hat Inc. Application Tuning GUI """ __author__ = "Arnaldo Carvalho de Melo " __license__ = "GPLv2 License" tuna-0.19/tuna/config.py000066400000000000000000000444421437350234000152110ustar00rootroot00000000000000import io import os import re import fnmatch import sys import codecs import configparser from time import localtime, strftime from subprocess import Popen, PIPE, STDOUT, call import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk TUNED_CONF = """[sysctl]\n""" class Config: # init config, load /etc/tuna.conf (if not exist, create it) def __init__(self): self.aliasList = [] self.aliasReverse = [] self.configFile = "/etc/tuna.conf" try: self.configParser = configparser.RawConfigParser() self.configParser.read(self.configFile) cfg = self.configParser.items('global') except configparser.Error: f = open(self.configFile, 'w') f.write("[global]\n") f.write("root=/etc/tuna/\n") f.write("lastFile=\n") f.close() self.configParser.read(self.configFile) cfg = self.configParser.items('global') self.config = {} for option, value in cfg: self.config[option] = value self.cacheFileName = '' def FileNameToConfigPath(self, filename): return filename.replace(".", "\\.").replace("/", ".") def ConfigPathToFileName(self, configpath): return configpath.replace(".", "/").replace("\\/", ".") def updateDefault(self, filename): if filename.replace("", "temp-direct-load.conf") != filename: self.temp = configparser.RawConfigParser() self.temp.read(self.configFile) self.temp.set('global', 'lastFile', filename) with open(self.configFile, 'w') as cfgfile: self.temp.write(cfgfile) self.config['lastfile'] = filename def load(self, profileName): tmp = configparser.RawConfigParser() tmp.read(self.config['root'] + profileName) try: check = tmp.items('categories') except configparser.NoSectionError: if self.tuned2Tuna(profileName) < 0: return -1 return self.loadTuna(profileName) def tuned2Tuna(self, profileName): try: tmp = configparser.RawConfigParser() tmp.read(self.config['root']+profileName) content = tmp.items('sysctl') f = open(self.config['root']+profileName, 'w') f.write("[categories]\n") f.write("sysctl=Tuned import\n") f.write("[sysctl]\n") for option, value in content: f.write(option + "=" + value + "\n") f.close() return 0 except (configparser.Error, IOError): dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, "%s\n%s" % (_("Corruputed config file: "), _(self.config['root']+profileName))) ret = dialog.run() dialog.destroy() return -1 def checkTunedDaemon(self): for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') tFile = os.path.join(path, "tuned") if os.path.isfile(tFile) and os.access(tFile, os.X_OK): return True return False def currentActiveProfile(self): proc = Popen(["tuned-adm", "active"], stdout=PIPE, stderr=PIPE) ret = proc.communicate() profile = ret[0] if profile and profile.find("Current active profile: ") == 0: return (profile[len("Current active profile: "):profile.find("\n")], ret[1]) return ("unknown", ret[1]) def setCurrentActiveProfile(self): call("tuned-adm profile tuna", shell=True) def saveTuned(self, data): ldir = "/etc/tuned/tuna" profile = self.currentActiveProfile() if profile[1]: raise RuntimeError( _("Can't activate tuna profile in tuned daemon\n%s" % profile[1])) # return False - unreachable code here! if not os.path.exists(ldir): try: os.stat(ldir) except (IOError, OSError): os.mkdir(ldir) f = codecs.open(os.path.join(ldir, "tuned.conf"), "w", "utf-8") f.write(TUNED_CONF) for index in data: for ind in data[index]: f.write(self.aliasToOriginal( data[index][ind]["label"])+"="+data[index][ind]["value"]+"\n") f.close() if profile[0] != "tuna": dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, "%s%s\n%s" % (_("Current active profile is: "), _(profile[0]), _("Set new created profile as current in tuned daemon?"))) ret = dialog.run() dialog.destroy() if ret == Gtk.ResponseType.YES: self.setCurrentActiveProfile() if self.currentActiveProfile()[0] != "tuna": raise RuntimeError("%s %s\n%s" % (_("Current active profile is: "), _(profile), _("Setting of new tuned profile failed! Check if tuned is installed and active"))) dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Tuna profile is now active in tuned daemon.")) ret = dialog.run() dialog.destroy() return True def loadTuna(self, profileName): print(f'profileName = {profileName}') # REMOVE err = self.checkConfigFile(self.config['root'] + profileName) if err != '': raise RuntimeError(_("Config file contain errors: ") + _(err)) try: self.configParser = configparser.RawConfigParser() self.configParser.read(self.config['root'] + profileName) tempCategories = self.configParser.items('categories') self.catIndex = 0 self.categoriesOrigin = {} self.categories = {} self.ctlParams = {} self.ctlGuiParams = {} self.aliasList = [] self.aliasReverse = [] for option, value in tempCategories: if value != "": oldTempCfg = self.configParser.items(option) self.ctlParams[self.catIndex] = {} self.ctlGuiParams[self.catIndex] = {} tempCfg = [] for index in range(len(oldTempCfg)): if self.isFnString(oldTempCfg[index][0]): expanded = self.getFilesByFN("/proc/sys", self.ConfigPathToFileName(oldTempCfg[index][0])) for index2 in range(len(expanded)): expandedData = (self.FileNameToConfigPath( expanded[index2]), oldTempCfg[index][1]) tempCfg.append(expandedData) else: tempCfg.append(oldTempCfg[index]) for opt, val in tempCfg: if val.find(',') != -1 and val.find(',', val.find(',')) != -1 and len(val.split(",")) > 2: self.ctlGuiParams[self.catIndex][opt] = val.split( ",") val = self.ctlGuiParams[self.catIndex][opt][2] sys = self.getSystemValue(opt) if val == "" or val == sys: self.ctlParams[self.catIndex][opt] = sys else: self.ctlParams[self.catIndex][opt] = val if opt in self.ctlGuiParams[self.catIndex]: if self.ctlGuiParams[self.catIndex][opt][0] == '': self.ctlGuiParams[self.catIndex][opt][0] = int( int(self.ctlParams[self.catIndex][opt])/10) else: self.ctlGuiParams[self.catIndex][opt][0] = int( self.ctlGuiParams[self.catIndex][opt][0]) if self.ctlGuiParams[self.catIndex][opt][1] == '': self.ctlGuiParams[self.catIndex][opt][1] = int( int(self.ctlParams[self.catIndex][opt])*10) else: self.ctlGuiParams[self.catIndex][opt][1] = int( self.ctlGuiParams[self.catIndex][opt][1]) self.categories[self.catIndex] = value self.categoriesOrigin[self.catIndex] = option self.catIndex = self.catIndex + 1 except (configparser.Error, IOError): print(_("Config file is corrupted")) return -1 try: self.aliasList = self.configParser.items('guiAlias') except configparser.Error: self.aliasList = [] self.aliasReverse = [] return 0 def updateDescription(self, filename): try: self.temp = configparser.RawConfigParser() self.temp.read(self.config['root'] + filename) self.description = self.temp.items('fileDescription') self.description = dict(self.description)['text'] except configparser.Error as e: self.description = _("Description for this profile not found") if e != configparser.NoSectionError: print(e) return self.description def fileToCache(self, profileName): try: f = open(self.config['root'] + profileName, 'r') except IOError: pass if f is None: raise RuntimeError(_("Cant open this config file: %s" % (self.config['root'] + profileName))) self.cacheFileName = profileName self.cache = f.read() f.close() self.updateDescription(profileName) def cacheToFile(self, profileName): try: f = open(self.config['root'] + profileName, 'w') f.write(self.cache) f.close() except IOError: print(_("Cant write to config file: %s" % (self.config['root'] + profileName))) def loadDirect(self, data): try: f = open(self.config['root']+"temp-direct-load.conf", 'w') except IOError: raise RuntimeError( _("Cant open this config file: %stemp-direct-load.conf" % (self.config['root']))) f.write(data) f.close() ret = self.load("temp-direct-load.conf") os.unlink(self.config['root']+"temp-direct-load.conf") return ret def populate(self): return [files for files in os.listdir(self.config['root']) if files != "temp-direct-load.conf"] def getSystemValue(self, filename): filename = self.aliasToOriginal(filename) try: buffer = open( "/proc/sys/" + self.ConfigPathToFileName(filename), 'r').read() except IOError: print(_("Invalid item! file: /proc/sys/%s" % (self.ConfigPathToFileName(filename)))) return "" return buffer.strip() def setSystemValue(self, filename, value): filename = self.aliasToOriginal(filename) old = self.getSystemValue(filename) if value == "" or old == value: return 0 try: fp = open("/proc/sys/" + self.ConfigPathToFileName(filename), 'w') fp.write(value) except IOError: print("%s%s %s %s" % (_("Cant write to file! path: /proc/sys/"), self.ConfigPathToFileName(filename), _("value:"), value)) return -1 return 0 def applyChanges(self, data): for cat in data: for itemId in data[cat]: self.setSystemValue( data[cat][itemId]['label'], data[cat][itemId]['value']) self.reloadSystemValues(data) def reloadSystemValues(self, data): for cat in self.ctlParams: for param in self.ctlParams[cat]: sys = self.getSystemValue(param) self.ctlParams[cat][param] = sys def aliasToOriginal(self, string): string = string.replace("*", "") if string in dict(self.aliasReverse): return dict(self.aliasReverse)[string] return string def originalToAlias(self, string): tmpString = string for src, dst in self.aliasList: tmpString = tmpString.replace(src, dst) if string != tmpString: self.aliasReverse[len(self.aliasReverse):] = [ (tmpString, string)] return tmpString return string def saveSnapshot(self, data): tempconfig = configparser.RawConfigParser() tempconfig.readfp(io.StringIO(self.cache)) snapcat = tempconfig.items('categories') out = {} for opt, val in snapcat: for index in range(len(data[val])): data[val][index]['label'] = self.aliasToOriginal( data[val][index]['label']) out[data[val][index]['label']] = data[val][index]['value'] for opt, val in snapcat: snapcontPacked = tempconfig.items(opt) snapcont = [] for index in range(len(snapcontPacked)): if self.isFnString(snapcontPacked[index][0]): expanded = self.getFilesByFN("/proc/sys", self.ConfigPathToFileName(snapcontPacked[index][0])) for index2 in range(len(expanded)): expandedData = (self.FileNameToConfigPath( expanded[index2]), snapcontPacked[index][1]) snapcont.append(expandedData) else: snapcont.append(snapcontPacked[index]) for iopt, ival in snapcont: if ival == '': tempconfig.set(opt, iopt, out[iopt]) elif ival == ',,': tempconfig.set(opt, iopt, ',,' + out[iopt]) else: reival = ival pos = [reival.start() for reival in re.finditer(',', reival)] if len(pos) == 2: ival = ival[0:pos[1]+1] tempconfig.set(opt, iopt, ival + out[iopt]) else: tempconfig.set(opt, iopt, out[iopt]) if 'lastfile' in self.config: self.name = self.config['lastfile'].replace('.conf', '') else: self.name = 'snapshot' snapFileName = self.config['root'] + self.name \ + strftime("-%Y-%m-%d-%H:%M:%S", localtime()) + '.conf' try: with open(snapFileName, 'w') as configfile: tempconfig.write(configfile) except IOError: print(_("Cant save snapshot")) return snapFileName def checkConfigFile(self, filename): self.empty = True try: msgStack = '' if not os.path.exists(filename): msgStack = "%s%s %s %s" % (msgStack, _( "Error: File"), filename, _("not found\n")) return msgStack self.checkParser = configparser.RawConfigParser() self.checkParser.read(filename) for option, value in self.checkParser.items('categories'): if not self.checkParser.items(option): msgStack = "%s%s %s\n" % (msgStack, _( "Error: Enabled section is empty:"), option) return msgStack current = self.checkParser.items(option) for opt, val in current: if not os.path.exists("/proc/sys/" + self.ConfigPathToFileName(opt)) and len(self.getFilesByFN("/proc/sys/", self.ConfigPathToFileName(opt))) == 0: msgStack = "%s%s%s\n" % (msgStack, _( "Warning: File not found: /proc/sys/"), opt) self.empty = False if self.empty: msgStack = "%s%s" % (msgStack, _("Empty config File")) return msgStack except (configparser.Error, IOError) as e: return "Error {0}".format(str(e)) def fixConfigFile(self, filename): try: self.checkParser = configparser.RawConfigParser() self.checkParser.read(filename) for option, value in self.checkParser.items('categories'): if not self.checkParser.items(option): self.checkParser.remove_option('categories', option) self.checkParser.set('categories', '#' + option, value) current = self.checkParser.items(option) for opt, val in current: if not os.path.exists("/proc/sys/" + self.ConfigPathToFileName(opt)) and len(self.getFilesByFN("/proc/sys/", self.ConfigPathToFileName(opt))) == 0: self.checkParser.remove_option(option, opt) self.checkParser.set(option, '#' + opt, val) except (configparser.Error, IOError) as e: return "Error {0}".format(str(e)) with open(filename, 'w') as configfile: self.checkParser.write(configfile) def isFnString(self, string): regMatch = ['[', '*', '?'] for char in regMatch: if char in string: return True return False def getFilesByFN(self, troot, fn): mylist = {} for root, dirs, files in os.walk(troot, topdown=True): for cfile in files: if fnmatch.fnmatch(root + "/" + cfile, "*" + fn): mylist[len(mylist)] = root.replace( troot, "")[1:] + "/" + cfile return mylist tuna-0.19/tuna/gui/000077500000000000000000000000001437350234000141465ustar00rootroot00000000000000tuna-0.19/tuna/gui/__init__.py000077500000000000000000000006111437350234000162600ustar00rootroot00000000000000""" Copyright (c) 2009 Red Hat Inc. Application Tuning GUI """ __author__ = "Arnaldo Carvalho de Melo " __license__ = "GPLv2 License" DND_TARGET_STRING = 0 DND_TARGET_ROOTWIN = 1 DND_TARGETS = [('STRING', 0, DND_TARGET_STRING), ('text/plain', 0, DND_TARGET_STRING), ('application/x-rootwin-drop', 0, DND_TARGET_ROOTWIN)] from .util import * tuna-0.19/tuna/gui/commonview.py000066400000000000000000000350011437350234000167020ustar00rootroot00000000000000from gi.repository import Gtk from tuna import tuna class commonview: def updateCommonView(self): try: self.contentTable self.config except: pass self.cleanUp() self.setup() def cleanUp(self): for value in self.contentTable.get_children(): if Gtk.Buildable.get_name(value) == "controls": self.ctrl = value if Gtk.Buildable.get_name(value) == "profileSelectorBox": self.selector = value self.contentTable.remove(value) def setup(self): try: self.contentTable.set_homogeneous(False) catListlenght = len(self.config.categories) if catListlenght <= 0: return False row = ((catListlenght+(catListlenght%2))/2)-catListlenght%2 frames = {} frameContent = {} catCntr = 0 contentCntr = 0 self.contentTable.resize(row+3, 2) self.contentTable.attach(self.ctrl, 0, 2, 1, 2, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL) self.contentTable.attach(self.selector, 0, 2, 0, 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL) cur = self.profileview.configFileCombo.get_model() for val in cur: if val[0] == self.config.cacheFileName: try: self.configFileCombo.handler_block_by_func(self.on_profileSelector_changed) except TypeError: pass self.configFileCombo.set_active(val.path[0]) try: self.configFileCombo.handler_unblock_by_func(self.on_profileSelector_changed) except TypeError as e: pass while catCntr < catListlenght: frames[catCntr] = Gtk.Frame() tLabel = Gtk.Label(label=''+self.config.categories[catCntr]+'') tLabel.set_use_markup(True) frames[catCntr].set_label_widget(tLabel) frameContent[catCntr] = {} frameContent[catCntr]['labels'] = {} frameContent[catCntr]['texts'] = {} frameContent[catCntr]['tooltips'] = {} currentCol = catCntr%2 currentRow = (catCntr/2)+2 if len(self.config.ctlParams[catCntr]) > 0: frameContent[catCntr]['table'] = Gtk.Table(len(self.config.ctlParams[catCntr]), 2, False) else: frameContent[catCntr]['table'] = Gtk.Table(1, 2, False) contentCntr = 0 for val in sorted(self.config.ctlParams[catCntr], key=str.lower): if self.config.getSystemValue(val) != self.config.ctlParams[catCntr][val]: star = "*" else: star = "" frameContent[catCntr]['labels'][contentCntr] = Gtk.Label(label=self.config.originalToAlias(val)+star) frameContent[catCntr]['labels'][contentCntr].set_alignment(0, 0.5) frameContent[catCntr]['tooltips'][contentCntr] = tuna.proc_sys_help(val) if len(frameContent[catCntr]['tooltips'][contentCntr]): frameContent[catCntr]['labels'][contentCntr].set_tooltip_text(frameContent[catCntr]['tooltips'][contentCntr]) if val in self.config.ctlGuiParams[catCntr]: # scale control frameContent[catCntr]['texts'][contentCntr] = Gtk.HScale() frameContent[catCntr]['texts'][contentCntr].set_range(self.config.ctlGuiParams[catCntr][val][0], self.config.ctlGuiParams[catCntr][val][1]) #frameContent[catCntr]['texts'][contentCntr].set_update_policy(Gtk.UPDATE_CONTINUOUS) frameContent[catCntr]['texts'][contentCntr].set_value(int(self.config.ctlParams[catCntr][val])) frameContent[catCntr]['texts'][contentCntr].set_digits(0) else: # input field frameContent[catCntr]['texts'][contentCntr] = Gtk.Entry() frameContent[catCntr]['texts'][contentCntr].set_max_length(256) frameContent[catCntr]['texts'][contentCntr].set_alignment(0) frameContent[catCntr]['texts'][contentCntr].set_text(self.config.ctlParams[catCntr][val]) frameContent[catCntr]['texts'][contentCntr].connect("button-release-event", self.checkStar, catCntr, contentCntr, val, frameContent[catCntr]['labels'][contentCntr]) frameContent[catCntr]['texts'][contentCntr].connect("focus-out-event", self.checkStar, catCntr, contentCntr, val, frameContent[catCntr]['labels'][contentCntr]) frameContent[catCntr]['table'].attach(frameContent[catCntr]['labels'][contentCntr], 0, 1, contentCntr, contentCntr+1, Gtk.AttachOptions.FILL, xpadding=5) frameContent[catCntr]['table'].attach(frameContent[catCntr]['texts'][contentCntr], 1, 2, contentCntr, contentCntr+1, xpadding=10) contentCntr = contentCntr+1 frames[catCntr].add(frameContent[catCntr]['table']) self.contentTable.attach(frames[catCntr], currentCol, currentCol+1, currentRow, currentRow+1, Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.FILL, 1, 1) catCntr = catCntr+1 self.ctrl.set_padding(5, 5, 0, 5) self.contentTable.set_border_width(5) self.contentTable.show_all() except AttributeError as e: return False def guiSnapshot(self): self.ret = {} self.property_cntr = 0 for value in self.contentTable.get_children(): if Gtk.Buildable.get_name(value) == "controls" or Gtk.Buildable.get_name(value) == "profileSelectorBox": continue self.ret[value.get_label()] = {} for content in value: if content.get_name() != "GtkTable": continue self.property_cntr = 0 for content_last in content.get_children(): if not content.child_get_property(content_last, "top-attach") in self.ret[value.get_label()]: self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")] = {} if content_last.get_name() == "GtkLabel": self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")]['label'] = content_last.get_label() else: if content_last.get_name() == "GtkEntry": self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")]['value'] = content_last.get_text() else: self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")]['value'] = str(int(content_last.get_value())) return self.ret def systemSnapshot(self): self.ret = {} self.property_cntr = 0 for value in self.contentTable.get_children(): if Gtk.Buildable.get_name(value) == "controls" or Gtk.Buildable.get_name(value) == "profileSelectorBox": continue self.ret[value.get_label()] = {} for content in value: if content.get_name() != "GtkTable": continue self.property_cntr = 0 for content_last in content.get_children(): if not content.child_get_property(content_last, "top-attach") in self.ret[value.get_label()]: self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")] = {} if content_last.get_name() == "GtkLabel": self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")]['label'] = content_last.get_label() self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")]['value'] = self.config.getSystemValue(self.ret[value.get_label()][content.child_get_property(content_last, "top-attach")]['label']) return self.ret def on_applyChanges_clicked(self, widget): self.config.backup = self.systemSnapshot() self.config.applyChanges(self.guiSnapshot()) self.updateCommonView() def on_undoChanges_clicked(self, widget): try: self.config.backup self.config.applyChanges(self.config.backup) self.updateCommonView() except: dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, \ _("Backup not found, this button is useable after click on apply")) ret = dialog.run() dialog.destroy() def on_saveSnapshot_clicked(self, widget): ret = self.guiSnapshot() self.config.saveSnapshot(self.ret) old_name = self.get_current_combo_selection() if self.profileview.setProfileFileList(): self.profileview.set_current_tree_selection(old_name[1]) self.set_current_combo_selection(old_name[1]) def on_saveTunedChanges_clicked(self, widget): if not self.config.checkTunedDaemon(): dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, \ _("Tuned daemon undetected!\nFor this function you must have installed Tuned daemon.")) ret = dialog.run() dialog.destroy() return False dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, \ _("This function can create new profile for tuned daemon and apply config permanently after reboot.\nProfile will be permanently saved and rewrite all old profiles created by tuna!\nUsing this only if you know that config cant corrupt your system!\nRealy can do it?")) ret = dialog.run() dialog.destroy() if ret == Gtk.ResponseType.NO: return False try: ret = self.guiSnapshot() self.config.saveTuned(ret) except RuntimeError as e: dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, str(e)) ret = dialog.run() dialog.destroy() self.profileview.setProfileFileList() def on_profileSelector_changed(self, widget): ret = self.get_current_combo_selection() if ret[0] < 0: return False self.restoreConfig = False err = self.config.checkConfigFile(self.config.config['root']+ret[1]) if err != '': self.restoreConfig = True dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, \ _("Config file contain errors: \n%s\nRun autocorrect?") % _(err)) dlgret = dialog.run() dialog.destroy() if dlgret == Gtk.ResponseType.YES: self.config.fixConfigFile(self.config.config['root'] + ret[1]) err = self.config.checkConfigFile(self.config.config['root'] + ret[1]) if err != '': dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, \ _("Config file contain errors: \n%s\nAutocorrect failed!") % _(err)) dialog.run() dialog.destroy() self.restoreConfig = True else: dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.INFO, Gtk.ButtonsType.OK, \ _("Autocorrect OK")) dialog.run() dialog.destroy() self.restoreConfig = False if self.restoreConfig: old = self.config.cacheFileName.rfind("/") old = self.config.cacheFileName[old+1:len(self.config.cacheFileName)] cur = self.configFileCombo.get_model() for val in cur: if val[0] == old: self.configFileCombo.handler_block_by_func(self.on_profileSelector_changed) self.configFileCombo.set_active(val.path[0]) self.configFileCombo.handler_unblock_by_func(self.on_profileSelector_changed) return False cur = self.profileview.configFileTree.get_model() for val in cur: if val[0] == ret[1]: self.configFileCombo.handler_block_by_func(self.on_profileSelector_changed) self.profileview.configFileTree.set_cursor(val.path[0]) self.configFileCombo.handler_unblock_by_func(self.on_profileSelector_changed) self.config.loadTuna(ret[1]) self.config.updateDefault(ret[1]) self.updateCommonView() return True def get_current_combo_selection(self): combo_iter = self.configFileCombo.get_active_iter() combo_row = self.configFileCombo.get_active() if combo_iter is not None: model = self.configFileCombo.get_model() return (combo_row, model[combo_iter][0]) return (-1, "ERROR") def set_current_combo_selection(self, string): cur = self.configFileCombo.get_model() for val in cur: if val[0] == string: self.configFileCombo.set_active(val.path[0]) def checkStar(self, widget, event, catCntr, contentCntr, val, label): lbl = label.get_label().replace("*", "") if widget.get_name() == "GtkEntry": value = widget.get_text() else: value = str(int(widget.get_value())) if value != self.config.getSystemValue(lbl): label.set_label(lbl+"*") else: label.set_label(lbl) tuna-0.19/tuna/gui/cpuview.py000077500000000000000000000313011437350234000162030ustar00rootroot00000000000000# -*- python -*- # -*- coding: utf-8 -*- from functools import reduce import math import os import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject import tuna.tuna_sched as tuna_sched import procfs from tuna import sysfs, tuna, gui def set_affinity_warning(tid, affinity): dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, \ Gtk.ButtonsType.OK, \ _("Couldn't change the affinity of %(tid)d to %(affinity)s!") % \ {"tid": tid, "affinity": affinity}) dialog.run() dialog.destroy() def drop_handler_move_threads_to_cpu(new_affinity, data): pid_list = [int(pid) for pid in data.split(",")] return tuna.move_threads_to_cpu(new_affinity, pid_list, set_affinity_warning) def drop_handler_move_irqs_to_cpu(cpus, data): irq_list = [int(irq) for irq in data.split(",")] new_affinity = [reduce(lambda a, b: a | b, [1 << cpu for cpu in cpus]),] for irq in irq_list: tuna.set_irq_affinity(irq, new_affinity) # FIXME: check if we really changed the affinity, but # its only an optimization to avoid a needless refresh # in the irqview, now we always refresh. return True class cpu_socket_frame(Gtk.Frame): (COL_FILTER, COL_CPU, COL_USAGE) = list(range(3)) def __init__(self, socket, cpus, creator): GObject.GObject.__init__(self) if creator.nr_sockets > 1: self.set_label(_("Socket %s") % socket) self.socket = socket self.cpus = cpus self.nr_cpus = len(cpus) self.creator = creator self.list_store = Gtk.ListStore(GObject.TYPE_BOOLEAN, GObject.TYPE_UINT, GObject.TYPE_UINT) self.treeview = Gtk.TreeView(self.list_store) # Filter column renderer = Gtk.CellRendererToggle() renderer.connect('toggled', self.filter_toggled, self.list_store) column = Gtk.TreeViewColumn(_('Filter'), renderer, active=self.COL_FILTER) self.treeview.append_column(column) # CPU# column column = Gtk.TreeViewColumn(_('CPU'), Gtk.CellRendererText(), text=self.COL_CPU) self.treeview.append_column(column) # CPU usage column try: column = Gtk.TreeViewColumn(_('Usage'), Gtk.CellRendererProgress(), text=self.COL_USAGE, value=self.COL_USAGE) except: # CellRendererProgress needs pygtk2 >= 2.6 column = Gtk.TreeViewColumn(_('Usage'), Gtk.CellRendererText(), text=self.COL_USAGE) self.treeview.append_column(column) self.add(self.treeview) self.treeview.enable_model_drag_dest(gui.DND_TARGETS, Gdk.DragAction.DEFAULT) self.treeview.connect("drag_data_received", self.on_drag_data_received_data) self.treeview.connect("button_press_event", self.on_cpu_socket_frame_button_press_event) self.drop_handlers = { "pid": (drop_handler_move_threads_to_cpu, self.creator.procview), "irq": (drop_handler_move_irqs_to_cpu, self.creator.irqview),} #self.drag_dest_set(Gtk.DestDefaults.ALL, gui.DND_TARGETS, # Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) self.connect("drag_data_received", self.on_frame_drag_data_received_data) def on_frame_drag_data_received_data(self, w, context, x, y, selection, info, etime): # Move to all CPUs in this socket cpus = [int(cpu.name[3:]) for cpu in self.cpus] # pid list, a irq list, etc source, data = selection.data.split(":") if source in self.drop_handlers: if self.drop_handlers[source][0](cpus, data): self.drop_handlers[source][1].refresh() else: print("cpu_socket_frame: unhandled drag source '%s'" % source) def on_drag_data_received_data(self, treeview, context, x, y, selection, info, etime): drop_info = treeview.get_dest_row_at_pos(x, y) # pid list, a irq list, etc source, data = selection.data.split(":") if drop_info: model = treeview.get_model() path, position = drop_info iter = model.get_iter(path) cpus = [model.get_value(iter, self.COL_CPU),] else: # Move to all CPUs in this socket cpus = [int(cpu.name[3:]) for cpu in self.cpus] if source in self.drop_handlers: if self.drop_handlers[source][0](cpus, data): self.drop_handlers[source][1].refresh() else: print("cpu_socket_frame: unhandled drag source '%s'" % source) def refresh(self): self.list_store.clear() for i in range(self.nr_cpus): cpu = self.cpus[i] cpunr = int(cpu.name[3:]) usage = self.creator.cpustats[cpunr + 1].usage iter = self.list_store.append() self.list_store.set( iter, self.COL_FILTER, cpunr not in self.creator.cpus_filtered, self.COL_CPU, cpunr, self.COL_USAGE, int(usage)) self.treeview.show_all() def isolate_cpu(self, a): ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) if not ret: return path, col, xpos, ypos = ret if not path: return row = self.list_store.get_iter(path) cpu = self.list_store.get_value(row, self.COL_CPU) self.creator.isolate_cpus([cpu,]) def include_cpu(self, a): ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) if not ret: return path, col, xpos, ypos = ret if not path: return row = self.list_store.get_iter(path) cpu = self.list_store.get_value(row, self.COL_CPU) self.creator.include_cpus([cpu,]) def restore_cpu(self, a): self.creator.restore_cpu() def isolate_cpu_socket(self, a): # Isolate all CPUs in this socket cpus = [int(cpu.name[3:]) for cpu in self.cpus] self.creator.isolate_cpus(cpus) def include_cpu_socket(self, a): # Include all CPUs in this socket cpus = [int(cpu.name[3:]) for cpu in self.cpus] self.creator.include_cpus(cpus) def on_cpu_socket_frame_button_press_event(self, treeview, event): if event.type != Gdk.EventType.BUTTON_PRESS or event.button != 3: return self.last_x = int(event.x) self.last_y = int(event.y) menu = Gtk.Menu() include = Gtk.MenuItem(_("I_nclude CPU"), use_underline = True) isolate = Gtk.MenuItem(_("_Isolate CPU"), use_underline = True) if self.creator.nr_sockets > 1: include_socket = Gtk.MenuItem(_("I_nclude CPU Socket"), use_underline = True) isolate_socket = Gtk.MenuItem(_("_Isolate CPU Socket"), use_underline = True) restore = Gtk.MenuItem(_("_Restore CPU"), use_underline = True) menu.add(include) menu.add(isolate) if self.creator.nr_sockets > 1: menu.add(include_socket) menu.add(isolate_socket) menu.add(restore) include.connect_object('activate', self.include_cpu, event) isolate.connect_object('activate', self.isolate_cpu, event) if self.creator.nr_sockets > 1: include_socket.connect_object('activate', self.include_cpu_socket, event) isolate_socket.connect_object('activate', self.isolate_cpu_socket, event) if not (self.creator.previous_pid_affinities or \ self.creator.previous_irq_affinities): restore.set_sensitive(False) restore.connect_object('activate', self.restore_cpu, event) include.show() isolate.show() if self.creator.nr_sockets > 1: include_socket.show() isolate_socket.show() restore.show() menu.popup(None, None, None, event, event.button, event.time) def filter_toggled(self, cell, path, model): # get toggled iter iter = model.get_iter((int(path),)) enabled = model.get_value(iter, self.COL_FILTER) cpu = model.get_value(iter, self.COL_CPU) enabled = not enabled self.creator.toggle_mask_cpu(cpu, enabled) # set new value model.set(iter, self.COL_FILTER, enabled) class cpuview: def __init__(self, vpaned, hpaned, window, procview, irqview, cpus_filtered, refresh_time): self.cpus = sysfs.cpus() self.cpustats = procfs.cpusstats() self.socket_frames = {} self.procview = procview self.irqview = irqview vbox = window.get_child().get_child() socket_ids = [] for id in list(self.cpus.sockets.keys()): try: socket_ids.append(int(id)) except TypeError: # Skip over offline cpus - type None continue socket_ids.sort() self.nr_sockets = len(socket_ids) if self.nr_sockets > 1: columns = math.ceil(math.sqrt(self.nr_sockets)) rows = math.ceil(self.nr_sockets / columns) box = Gtk.HBox() vbox.pack_start(box, False, False, 2) else: box = vbox column = 1 for socket_id in socket_ids: frame = cpu_socket_frame(socket_id, self.cpus.sockets[str(socket_id)], self) box.pack_start(frame, False, False, 2) self.socket_frames[socket_id] = frame if self.nr_sockets > 1: if column == columns: box = Gtk.HBox() vbox.pack_start(box, False, False, 2) column = 1 else: column += 1 window.show_all() self.cpus_filtered = cpus_filtered self.refresh() self.previous_pid_affinities = None self.previous_irq_affinities = None #req = frame.size_request() req = frame.get_preferred_size() # FIXME: what is the slack we have # to add to every row and column? width = req.minimum_size.width + 16 height = req.minimum_size.height + 40 if self.nr_sockets > 1: width *= columns height *= rows vpaned.set_position(int(height)) hpaned.set_position(int(width)) self.timer = GObject.timeout_add(refresh_time, self.refresh) def isolate_cpus(self, cpus): self.previous_pid_affinities, \ self.previous_irq_affinities = tuna.isolate_cpus(cpus, self.cpus.nr_cpus) if self.previous_pid_affinities: self.procview.refresh() if self.previous_irq_affinities: self.irqview.refresh() def include_cpus(self, cpus): self.previous_pid_affinities, \ self.previous_irq_affinities = tuna.include_cpus(cpus, self.cpus.nr_cpus) if self.previous_pid_affinities: self.procview.refresh() if self.previous_irq_affinities: self.irqview.refresh() def restore_cpu(self): if not (self.previous_pid_affinities or \ self.previous_irq_affinities): return affinities = self.previous_pid_affinities for pid in list(affinities.keys()): try: os.sched_setaffinity(pid, affinities[pid]) except: pass affinities = self.previous_irq_affinities for irq in list(affinities.keys()): tuna.set_irq_affinity(int(irq), procfs.hexbitmask(affinities[irq], \ self.cpus.nr_cpus)) self.previous_pid_affinities = None self.previous_irq_affinities = None def toggle_mask_cpu(self, cpu, enabled): if enabled: if cpu in self.cpus_filtered: self.cpus_filtered.remove(cpu) else: if cpu not in self.cpus_filtered: self.cpus_filtered.append(cpu) self.procview.toggle_mask_cpu(cpu, enabled) self.irqview.toggle_mask_cpu(cpu, enabled) def refresh(self): self.cpustats.reload() for frame in list(self.socket_frames.keys()): self.socket_frames[frame].refresh() return True tuna-0.19/tuna/gui/irqview.py000077500000000000000000000307041437350234000162150ustar00rootroot00000000000000# -*- python -*- # -*- coding: utf-8 -*- from tuna import tuna, gui import procfs from gi.repository import Gdk from gi.repository import Gtk from gi.repository import GObject import os from functools import reduce import tuna.new_eth as ethtool import tuna.tuna_sched as tuna_sched import gi gi.require_version("Gtk", "3.0") class irq_druid: def __init__(self, irqs, ps, irq, gladefile): self.irqs = irqs self.ps = ps self.irq = irq self.window = Gtk.Builder() self.window.add_objects_from_file(gladefile, ("set_irq_attributes", "tuna")) #self.window = Gtk.glade.XML(gladefile, "set_irq_attributes", "tuna") self.dialog = self.window.get_object("set_irq_attributes") pixbuf = self.dialog.render_icon(Gtk.STOCK_PREFERENCES, Gtk.IconSize.SMALL_TOOLBAR) self.dialog.set_icon(pixbuf) event_handlers = { "on_irq_affinity_text_changed": self.on_irq_affinity_text_changed, "on_sched_policy_combo_changed": self.on_sched_policy_combo_changed} # self.window.signal_autoconnect(event_handlers) self.window.connect_signals(event_handlers) self.sched_pri = self.window.get_object("irq_pri_spinbutton") self.sched_pri.set_range(0, 99) self.sched_policy = self.window.get_object("irq_policy_combobox") self.affinity = self.window.get_object("irq_affinity_text") text = self.window.get_object("irq_text") users = tuna.get_irq_users(irqs, irq) self.affinity_text = tuna.get_irq_affinity_text(irqs, irq) irq_re = tuna.threaded_irq_re(irq) pids = self.ps.find_by_regex(irq_re) if pids: pid = pids[0] prio = int(ps[pid]["stat"]["rt_priority"]) self.create_policy_model(self.sched_policy) self.sched_policy.set_active(os.sched_getscheduler(pid)) self.sched_pri.set_value(prio) text.set_markup( "IRQ %u (PID %u), pri %u, aff %s, %s" % (irq, pid, prio, self.affinity_text, ",".join(users))) else: self.sched_pri.set_sensitive(False) self.sched_policy.set_sensitive(False) text.set_markup( "IRQ %u, aff %s, %s" % (irq, self.affinity_text, ",".join(users))) self.affinity.set_text(self.affinity_text) def create_policy_model(self, policy): (COL_TEXT, COL_SCHED) = list(range(2)) list_store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_UINT) renderer = Gtk.CellRendererText() policy.pack_start(renderer, True) policy.add_attribute(renderer, "text", COL_TEXT) for pol in range(4): row = list_store.append() list_store.set(row, COL_TEXT, tuna_sched.sched_str(pol), COL_SCHED, pol) policy.set_model(list_store) def on_sched_policy_combo_changed(self, button): new_policy = self.sched_policy.get_active() if new_policy in (os.SCHED_FIFO, os.SCHED_RR): can_change_pri = True else: can_change_pri = False self.sched_pri.set_sensitive(can_change_pri) def on_irq_affinity_text_changed(self, button): gui.on_affinity_text_changed(self) def run(self): changed = False if self.dialog.run() == Gtk.ResponseType.OK: new_policy = self.sched_policy.get_active() new_prio = int(self.sched_pri.get_value()) new_affinity = self.affinity.get_text() irq_re = tuna.threaded_irq_re(self.irq) pids = self.ps.find_by_regex(irq_re) if pids: if gui.thread_set_attributes(self.ps[pids[0]], new_policy, new_prio, new_affinity, self.irqs.nr_cpus): changed = True try: new_affinity = [int(a) for a in new_affinity.split(",")] except: try: new_affinity = tuna.cpustring_to_list(new_affinity) except: new_affinity = procfs.bitmasklist(new_affinity, self.irqs.nr_cpus) new_affinity.sort() curr_affinity = self.irqs[self.irq]["affinity"] if curr_affinity != new_affinity: tuna.set_irq_affinity( self.irq, procfs.hexbitmask(new_affinity, self.irqs.nr_cpus)) changed = True self.dialog.destroy() return changed class irqview: nr_columns = 7 (COL_NUM, COL_PID, COL_POL, COL_PRI, COL_AFF, COL_EVENTS, COL_USERS) = list(range(nr_columns)) columns = (gui.list_store_column(_("IRQ")), gui.list_store_column(_("PID"), GObject.TYPE_INT), gui.list_store_column(_("Policy"), GObject.TYPE_STRING), gui.list_store_column(_("Priority"), GObject.TYPE_INT), gui.list_store_column(_("Affinity"), GObject.TYPE_STRING), gui.list_store_column(_("Events")), gui.list_store_column(_("Users"), GObject.TYPE_STRING)) def __init__(self, treeview, irqs, ps, cpus_filtered, gladefile): self.is_root = os.getuid() == 0 self.irqs = irqs self.ps = ps self.treeview = treeview self.gladefile = gladefile self.has_threaded_irqs = tuna.has_threaded_irqs(ps) if not self.has_threaded_irqs: self.nr_columns = 4 (self.COL_NUM, self.COL_AFF, self.COL_EVENTS, self.COL_USERS) = list(range(self.nr_columns)) self.columns = (gui.list_store_column(_("IRQ")), gui.list_store_column( _("Affinity"), GObject.TYPE_STRING), gui.list_store_column(_("Events")), gui.list_store_column(_("Users"), GObject.TYPE_STRING)) self.list_store = Gtk.ListStore( *gui.generate_list_store_columns_with_attr(self.columns)) # Allow selecting multiple rows selection = treeview.get_selection() selection.set_mode(Gtk.SelectionMode.MULTIPLE) # Allow enable drag and drop of rows self.treeview.enable_model_drag_source( Gdk.ModifierType.BUTTON1_MASK, gui.DND_TARGETS, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) self.treeview.connect("drag_data_get", self.on_drag_data_get_data) self.renderer = Gtk.CellRendererText() for col in range(self.nr_columns): column = Gtk.TreeViewColumn(self.columns[col].name, self.renderer, text=col) column.set_sort_column_id(col) column.add_attribute(self.renderer, "weight", col + self.nr_columns) self.treeview.append_column(column) self.cpus_filtered = cpus_filtered self.refreshing = True self.treeview.set_model(self.list_store) def foreach_selected_cb(self, model, path, iter, irq_list): irq = model.get_value(iter, self.COL_NUM) irq_list.append(str(irq)) def on_drag_data_get_data(self, treeview, context, selection, target_id, etime): treeselection = treeview.get_selection() irq_list = [] treeselection.selected_foreach(self.foreach_selected_cb, irq_list) selection.set(selection.target, 8, "irq:" + ",".join(irq_list)) def set_irq_columns(self, iter, irq, irq_info, nics): new_value = [None] * self.nr_columns users = tuna.get_irq_users(self.irqs, irq, nics) if self.has_threaded_irqs: irq_re = tuna.threaded_irq_re(irq) pids = self.ps.find_by_regex(irq_re) if pids: pid = pids[0] prio = int(self.ps[pid]["stat"]["rt_priority"]) sched = tuna_sched.sched_str(os.sched_getscheduler(pid))[6:] else: sched = "" pid = -1 prio = -1 new_value[self.COL_PID] = pid new_value[self.COL_POL] = sched new_value[self.COL_PRI] = prio new_value[self.COL_NUM] = irq new_value[self.COL_AFF] = tuna.get_irq_affinity_text(self.irqs, irq) new_value[self.COL_EVENTS] = reduce( lambda a, b: a + b, irq_info["cpu"]) new_value[self.COL_USERS] = ",".join(users) gui.set_store_columns(self.list_store, iter, new_value) def show(self): new_irqs = [] for sirq in list(self.irqs.keys()): try: new_irqs.append(int(sirq)) except: continue nics = ethtool.get_active_devices() row = self.list_store.get_iter_first() while row: irq = self.list_store.get_value(row, self.COL_NUM) # IRQ was unregistered? I.e. driver unloaded? if irq not in self.irqs: if self.list_store.remove(row): # removed and row now its the next one continue # Was the last one break if tuna.irq_filtered(irq, self.irqs, self.cpus_filtered, self.is_root): new_irqs.remove(irq) if self.list_store.remove(row): # removed and row now its the next one continue # Was the last one break try: new_irqs.remove(irq) irq_info = self.irqs[irq] self.set_irq_columns(row, irq, irq_info, nics) except: if self.list_store.remove(row): # removed and row now its the next one continue # Was the last one break row = self.list_store.iter_next(row) new_irqs.sort() for irq in new_irqs: if tuna.irq_filtered(irq, self.irqs, self.cpus_filtered, self.is_root): continue row = self.list_store.append() irq_info = self.irqs[irq] try: self.set_irq_columns(row, irq, irq_info, nics) except: self.list_store.remove(row) self.treeview.show_all() def refresh(self): if not self.refreshing: return self.irqs.reload() self.show() def refresh_toggle(self, unused): self.refreshing = not self.refreshing def edit_attributes(self, a): ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) if not ret: return path, col, xpos, ypos = ret if not path: return row = self.list_store.get_iter(path) irq = self.list_store.get_value(row, self.COL_NUM) if irq not in self.irqs: return dialog = irq_druid(self.irqs, self.ps, irq, self.gladefile) if dialog.run(): self.refresh() def on_irqlist_button_press_event(self, treeview, event): if event.type != Gdk.EventType.BUTTON_PRESS or event.button != 3: return self.last_x = int(event.x) self.last_y = int(event.y) menu = Gtk.Menu() setattr = Gtk.MenuItem(_("_Set IRQ attributes"), use_underline = True) if self.refreshing: refresh = Gtk.MenuItem(_("Sto_p refreshing the IRQ list"), use_underline = True) else: refresh = Gtk.MenuItem(_("_Refresh the IRQ list"), use_underline = True) menu.add(setattr) menu.add(refresh) setattr.connect_object('activate', self.edit_attributes, event) refresh.connect_object('activate', self.refresh_toggle, event) setattr.show() refresh.show() menu.popup(None, None, None, event, event.button, event.time) def toggle_mask_cpu(self, cpu, enabled): if not enabled: if cpu not in self.cpus_filtered: self.cpus_filtered.append(cpu) self.show() else: if cpu in self.cpus_filtered: self.cpus_filtered.remove(cpu) self.show() tuna-0.19/tuna/gui/procview.py000077500000000000000000000702741437350234000163730ustar00rootroot00000000000000import re import os import tuna.tuna_sched as tuna_sched import gi gi.require_version("Gtk", "3.0") from gi.repository import GObject from gi.repository import Gtk from gi.repository import Gdk from tuna import tuna, gui import procfs try: import perf except: pass def N_(s): """gettext_noop""" return s class process_druid: (PROCESS_COL_PID, PROCESS_COL_NAME) = list(range(2)) def __init__(self, ps, pid, pid_info, nr_cpus, gladefile): self.ps = ps self.pid = pid self.pid_info = pid_info self.nr_cpus = nr_cpus self.window = Gtk.Builder() self.window.add_objects_from_file(gladefile, ("set_process_attributes", "tuna")) # self.window = Gtk.glade.XML(gladefile, "set_process_attributes", "tuna") self.dialog = self.window.get_object("set_process_attributes") pixbuf = self.dialog.render_icon(Gtk.STOCK_PREFERENCES, Gtk.IconSize.SMALL_TOOLBAR) self.dialog.set_icon(pixbuf) event_handlers = { "on_cmdline_regex_changed" : self.on_cmdline_regex_changed, "on_affinity_text_changed" : self.on_affinity_text_changed, "on_sched_policy_combo_changed" : self.on_sched_policy_combo_changed, "on_command_regex_clicked" : self.on_command_regex_clicked, "on_all_these_threads_clicked" : self.on_all_these_threads_clicked, "on_just_this_thread_clicked" : self.on_just_this_thread_clicked} #self.window.signal_autoconnect(event_handlers) self.window.connect_signals(event_handlers) self.sched_pri = self.window.get_object("sched_pri_spin") self.sched_pri.set_range(0, 99) self.sched_policy = self.window.get_object("sched_policy_combo") self.regex_edit = self.window.get_object("cmdline_regex") self.affinity = self.window.get_object("affinity_text") self.just_this_thread = self.window.get_object("just_this_thread") self.all_these_threads = self.window.get_object("all_these_threads") processes = self.window.get_object("matching_process_list") self.sched_pri.set_value(int(pid_info["stat"]["rt_priority"])) cmdline_regex = procfs.process_cmdline(pid_info) self.affinity_text = tuna.list_to_cpustring(os.sched_getaffinity(pid)) self.affinity.set_text(self.affinity_text) self.create_matching_process_model(processes) self.create_policy_model(self.sched_policy) self.sched_policy.set_active(os.sched_getscheduler(pid)) self.regex_edit.set_text(cmdline_regex) self.just_this_thread.set_active(True) self.regex_edit.set_sensitive(False) if pid not in ps or "threads" not in ps[pid]: self.all_these_threads.hide() self.on_just_this_thread_clicked(None) def refresh_match_pids(self, cmdline_regex): self.process_list_store.clear() for match_pid in self.ps.find_by_cmdline_regex(cmdline_regex): info = self.process_list_store.append() pid_info = self.ps[match_pid] cmdline = procfs.process_cmdline(pid_info) self.process_list_store.set(info, self.PROCESS_COL_PID, match_pid, self.PROCESS_COL_NAME, cmdline) def create_matching_process_model(self, processes): labels = ["PID", "Name"] self.process_list_store = Gtk.ListStore(GObject.TYPE_UINT, GObject.TYPE_STRING) renderer = Gtk.CellRendererText() for col in range(len(labels)): column = Gtk.TreeViewColumn(labels[col], renderer, text=col) column.set_sort_column_id(col) processes.append_column(column) processes.set_model(self.process_list_store) def create_policy_model(self, policy): (COL_TEXT, COL_SCHED) = list(range(2)) list_store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_UINT) renderer = Gtk.CellRendererText() policy.pack_start(renderer, True) policy.add_attribute(renderer, "text", COL_TEXT) for pol in range(4): row = list_store.append() list_store.set(row, COL_TEXT, tuna_sched.sched_str(pol), COL_SCHED, pol) policy.set_model(list_store) def on_cmdline_regex_changed(self, entry): process_regex_text = entry.get_text() try: cmdline_regex = re.compile(process_regex_text) except: self.process_list_store.clear() return self.refresh_match_pids(cmdline_regex) def on_just_this_thread_clicked(self, button): self.regex_edit.set_sensitive(False) self.process_list_store.clear() info = self.process_list_store.append() cmdline = procfs.process_cmdline(self.pid_info) self.process_list_store.set(info, self.PROCESS_COL_PID, self.pid, self.PROCESS_COL_NAME, cmdline) def on_command_regex_clicked(self, button): self.regex_edit.set_sensitive(True) self.on_cmdline_regex_changed(self.regex_edit) def on_all_these_threads_clicked(self, button): self.regex_edit.set_sensitive(False) self.process_list_store.clear() info = self.process_list_store.append() cmdline = procfs.process_cmdline(self.ps[self.pid]) self.process_list_store.set(info, self.PROCESS_COL_PID, self.pid, self.PROCESS_COL_NAME, cmdline) for tid in list(self.ps[self.pid]["threads"].keys()): child = self.process_list_store.append() self.process_list_store.set(child, self.PROCESS_COL_PID, tid, self.PROCESS_COL_NAME, cmdline) def on_sched_policy_combo_changed(self, button): new_policy = self.sched_policy.get_active() if new_policy in (os.SCHED_FIFO, os.SCHED_RR): can_change_pri = True else: can_change_pri = False self.sched_pri.set_sensitive(can_change_pri) def on_affinity_text_changed(self, button): gui.on_affinity_text_changed(self) def set_attributes_for_regex(self, regex, new_policy, new_prio, new_affinity): changed = False cmdline_regex = re.compile(regex) for match_pid in self.ps.find_by_cmdline_regex(cmdline_regex): if gui.thread_set_attributes(self.ps[match_pid], new_policy, new_prio, new_affinity, self.nr_cpus): changed = True return changed def set_attributes_for_threads(self, pid, new_policy, new_prio, new_affinity): changed = False threads = self.ps[pid]["threads"] for tid in list(threads.keys()): if gui.thread_set_attributes(threads[tid], new_policy, new_prio, new_affinity, self.nr_cpus): changed = True return changed def run(self): changed = False if self.dialog.run() == Gtk.ResponseType.OK: new_policy = int(self.sched_policy.get_active()) new_prio = int(self.sched_pri.get_value()) new_affinity = self.affinity.get_text() if self.just_this_thread.get_active(): changed = gui.thread_set_attributes(self.pid_info, new_policy, new_prio, new_affinity, self.nr_cpus) elif self.all_these_threads.get_active(): if gui.thread_set_attributes(self.pid_info, new_policy, new_prio, new_affinity, self.nr_cpus): changed = True if self.set_attributes_for_threads(self.pid, new_policy, new_prio, new_affinity): changed = True else: changed = self.set_attributes_for_regex( self.regex_edit.get_text(), new_policy, new_prio, new_affinity) self.dialog.destroy() return changed class procview: nr_columns = 8 (COL_PID, COL_POL, COL_PRI, COL_AFF, COL_VOLCTXT, COL_NONVOLCTXT, COL_CGROUP, COL_CMDLINE) = list(range(nr_columns)) columns = (gui.list_store_column(_("PID")), gui.list_store_column(_("Policy"), GObject.TYPE_STRING), gui.list_store_column(_("Priority")), gui.list_store_column(_("Affinity"), GObject.TYPE_STRING), gui.list_store_column(_("VolCtxtSwitch"), GObject.TYPE_UINT), gui.list_store_column(_("NonVolCtxtSwitch"), GObject.TYPE_UINT), gui.list_store_column(_("CGroup"), GObject.TYPE_STRING), gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) def __init__(self, treeview, ps, show_kthreads, show_uthreads, cpus_filtered, gladefile, disable_perf): self.ps = ps self.treeview = treeview self.nr_cpus = procfs.cpuinfo().nr_cpus self.gladefile = gladefile self.evlist_added = True self.evlist = None if not disable_perf: try: self.perf_init() except: # No perf, poll /proc baby, poll pass if "voluntary_ctxt_switches" not in ps[1]["status"]: self.nr_columns = 5 else: self.nr_columns = 7 try: if ps[1]["cgroups"]: self.nr_columns = self.nr_columns + 1 except: pass self.columns = (gui.list_store_column(_("PID")), gui.list_store_column(_("Policy"), GObject.TYPE_STRING), gui.list_store_column(_("Priority")), gui.list_store_column(_("Affinity"), GObject.TYPE_STRING)) if self.nr_columns == 5: (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_CMDLINE) = list(range(self.nr_columns)) self.columns = self.columns \ + (gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) elif self.nr_columns == 6: (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_CGROUP, self.COL_CMDLINE) = list(range(self.nr_columns)) self.columns = self.columns \ + (gui.list_store_column(_("CGroup"), GObject.TYPE_STRING), gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) elif self.nr_columns == 7: (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, \ self.COL_VOLCTXT, self.NONVOLCTXT, self.COL_CMDLINE) \ = list(range(self.nr_columns)) self.columns = self.columns \ + (gui.list_store_column(_("VolCtxtSwitch"), GObject.TYPE_UINT), gui.list_store_column(_("NonVolCtxtSwitch"), GObject.TYPE_UINT), gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) elif self.nr_columns == 8: (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, \ self.COL_VOLCTXT, self.COL_NONVOLCTXT, self.COL_CGROUP, \ self.COL_CMDLINE) = list(range(self.nr_columns)) self.columns = self.columns \ + (gui.list_store_column(_("VolCtxtSwitch"), GObject.TYPE_UINT), gui.list_store_column(_("NonVolCtxtSwitch"), GObject.TYPE_UINT), gui.list_store_column(_("CGroup"), GObject.TYPE_STRING), gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) self.tree_store = Gtk.TreeStore(*gui.generate_list_store_columns_with_attr(self.columns)) self.treeview.set_model(self.tree_store) # Allow selecting multiple rows selection = treeview.get_selection() selection.set_mode(Gtk.SelectionMode.MULTIPLE) # Allow enable drag and drop of rows self.treeview.enable_model_drag_source( Gdk.ModifierType.BUTTON1_MASK, gui.DND_TARGETS, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) self.treeview.connect("drag_data_get", self.on_drag_data_get_data) try: self.treeview.connect("query-tooltip", self.on_query_tooltip) except: # old versions of pygtk2+ doesn't have this signal pass self.renderer = Gtk.CellRendererText() for col in range(self.nr_columns): column = Gtk.TreeViewColumn(self.columns[col].name, self.renderer, text=col) column.add_attribute(self.renderer, "weight", col + self.nr_columns) column.set_sort_column_id(col) if col == self.COL_CGROUP: column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) column.set_fixed_width(130) try: self.treeview.set_tooltip_column(col) except: # old versions of pygtk2+ doesn't have this signal pass column.set_resizable(True) self.treeview.append_column(column) self.show_kthreads = show_kthreads self.show_uthreads = show_uthreads self.cpus_filtered = cpus_filtered self.refreshing = True def perf_process_events(self, source, condition): had_events = True while had_events: had_events = False for cpu in self.cpu_map: event = self.evlist.read_on_cpu(cpu) if event: had_events = True if event.type == perf.RECORD_FORK: if event.pid == event.tid: try: self.ps.processes[event.pid] = procfs.process(event.pid) except: # short lived thread pass else: if event.pid in self.ps.processes: try: self.ps.processes[event.pid].threads.processes[event.tid] = procfs.process(event.tid) except (AttributeError, KeyError): try: self.ps.processes[event.pid].threads = procfs.pidstats("/proc/%d/task/" % event.pid) except: pass elif event.type == perf.RECORD_EXIT: del self.ps[int(event.tid)] elif event.type == perf.RECORD_SAMPLE: tid = event.sample_tid if tid in self.perf_counter: self.perf_counter[tid] += event.sample_period else: self.perf_counter[tid] = event.sample_period self.evlist_added = True # Mark that event arrived, so next periodic show() will refresh GUI return True def perf_init(self): self.cpu_map = perf.cpu_map() self.thread_map = perf.thread_map() self.evsel_cycles = perf.evsel(task=1, comm=1, wakeup_events=1, \ watermark=1, sample_type=perf.SAMPLE_CPU | perf.SAMPLE_TID) self.evsel_cycles.open(cpus=self.cpu_map, threads=self.thread_map) self.evlist = perf.evlist(self.cpu_map, self.thread_map) self.evlist.add(self.evsel_cycles) self.evlist.mmap() self.pollfd = self.evlist.get_pollfd() for f in self.pollfd: GObject.io_add_watch(f, GObject.IO_IN, self.perf_process_events) self.perf_counter = {} def on_query_tooltip(self, treeview, x, y, keyboard_mode, tooltip): x, y = treeview.convert_widget_to_bin_window_coords(x, y) ret = treeview.get_path_at_pos(x, y) tooltip.set_text(None) if not ret: return True path, col, xpos, ypos = ret if not path: return True col_id = col.get_sort_column_id() if col_id != self.COL_CMDLINE: return True row = self.tree_store.get_iter(path) if not row: return True pid = int(self.tree_store.get_value(row, self.COL_PID)) if not tuna.iskthread(pid): return True cmdline = self.tree_store.get_value(row, self.COL_CMDLINE).split(' ')[0] help = tuna.kthread_help(cmdline) tooltip.set_markup("%s %d(%s)\n%s" % \ (_("Kernel Thread"), pid, cmdline, _(help))) return True def foreach_selected_cb(self, model, path, iter, pid_list): pid = model.get_value(iter, self.COL_PID) pid_list.append(str(pid)) def on_drag_data_get_data(self, treeview, context, selection, target_id, etime): treeselection = treeview.get_selection() pid_list = [] treeselection.selected_foreach(self.foreach_selected_cb, pid_list) selection.set(selection.target, 8, "pid:" + ",".join(pid_list)) def set_thread_columns(self, iter, tid, thread_info): new_value = [None] * self.nr_columns new_value[self.COL_PRI] = int(thread_info["stat"]["rt_priority"]) new_value[self.COL_POL] = tuna_sched.sched_str(os.sched_getscheduler(tid))[6:] thread_affinity_list = os.sched_getaffinity(tid) new_value[self.COL_PID] = tid new_value[self.COL_AFF] = tuna.list_to_cpustring(thread_affinity_list) try: new_value[self.COL_VOLCTXT] = int(thread_info["status"]["voluntary_ctxt_switches"]) new_value[self.COL_NONVOLCTXT] \ = int(thread_info["status"]["nonvoluntary_ctxt_switches"]) new_value[self.COL_CGROUP] = thread_info["cgroups"] except: pass new_value[self.COL_CMDLINE] = procfs.process_cmdline(thread_info) gui.set_store_columns(self.tree_store, iter, new_value) def show(self, force_refresh=False): # Start with the first row, if there is one, on the # process list. If the first time update_rows will just # have everything in new_tids and append_new_tids will # create the rows. if not self.refreshing and not force_refresh: return # If using perf only refresh if we saw at least a new event since last refresh if not self.evlist or self.evlist_added: self.evlist_added = None row = self.tree_store.get_iter_first() self.update_rows(self.ps, row, None) self.treeview.show_all() def update_rows(self, threads, row, parent_row): new_tids = list(threads.keys()) previous_row = None while row: tid = self.tree_store.get_value(row, self.COL_PID) if previous_row: previous_tid = self.tree_store.get_value(previous_row, self.COL_PID) if previous_tid == tid: # print "WARNING: tree_store dup %d, fixing..." % tid self.tree_store.remove(previous_row) if tid not in threads: if self.tree_store.remove(row): # removed and now row is the next one continue # removed and its the last one break try: new_tids.remove(tid) except: # FIXME: understand in what situation this # can happen, seems harmless from visual # inspection. pass if tuna.thread_filtered(tid, self.cpus_filtered, self.show_kthreads, self.show_uthreads): if self.tree_store.remove(row): # removed and now row is the next one continue # removed and its the last one break else: try: self.set_thread_columns(row, tid, threads[tid]) if "threads" in threads[tid]: children = threads[tid]["threads"] else: children = {} child_row = self.tree_store.iter_children(row) self.update_rows(children, child_row, row) except: # thread doesn't exists anymore if self.tree_store.remove(row): # removed and now row is the next one continue # removed and its the last one break previous_row = row row = self.tree_store.iter_next(row) new_tids.sort() self.append_new_tids(parent_row, threads, new_tids) def append_new_tids(self, parent_row, threads, tid_list): for tid in tid_list: if tuna.thread_filtered(tid, self.cpus_filtered, self.show_kthreads, self.show_uthreads): continue row = self.tree_store.append(parent_row) try: self.set_thread_columns(row, tid, threads[tid]) except: # Thread doesn't exists anymore self.tree_store.remove(row) continue if "threads" in threads[tid]: children = threads[tid]["threads"] children_list = sorted(children.keys()) for child in children_list: child_row = self.tree_store.append(row) try: self.set_thread_columns(child_row, child, children[child]) except: # Thread doesn't exists anymore self.tree_store.remove(child_row) def refresh(self): self.ps.reload() self.ps.reload_threads() self.show(True) def edit_attributes(self, a): ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) if not ret: return path, col, xpos, ypos = ret if not path: return row = self.tree_store.get_iter(path) pid = self.tree_store.get_value(row, self.COL_PID) if pid in self.ps: pid_info = self.ps[pid] else: parent = self.tree_store.iter_parent(row) ppid = self.tree_store.get_value(parent, self.COL_PID) pid_info = self.ps[ppid].threads[pid] dialog = process_druid(self.ps, pid, pid_info, self.nr_cpus, self.gladefile) if dialog.run(): self.refresh() def kthreads_view_toggled(self, a): self.show_kthreads = not self.show_kthreads self.show(True) def uthreads_view_toggled(self, a): self.show_uthreads = not self.show_uthreads self.show(True) def help_dialog(self, a): ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) if not ret: return path, col, xpos, ypos = ret if not path: return row = self.tree_store.get_iter(path) pid = self.tree_store.get_value(row, self.COL_PID) if pid not in self.ps: return cmdline = self.tree_store.get_value(row, self.COL_CMDLINE) help, title = tuna.kthread_help_plain_text(pid, cmdline) dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _(help)) dialog.set_title(title) ret = dialog.run() dialog.destroy() def refresh_toggle(self, a): self.refreshing = not self.refreshing def save_kthreads_tunings(self, a): dialog = Gtk.FileChooserDialog(_("Save As"), None, \ Gtk.FileChooserAction.SAVE, \ (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, \ Gtk.STOCK_OK, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) try: dialog.set_do_overwrite_confirmation(True) except: pass filter = Gtk.FileFilter() filter.set_name("rtctl config files") filter.add_pattern("*.rtctl") filter.add_pattern("*.tuna") filter.add_pattern("*rtgroup*") dialog.add_filter(filter) filter = Gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") dialog.add_filter(filter) response = dialog.run() filename = dialog.get_filename() dialog.destroy() if response != Gtk.ResponseType.OK: return self.refresh() kthreads = tuna.get_kthread_sched_tunings(self.ps) tuna.generate_rtgroups(filename, kthreads, self.nr_cpus) if filename != "/etc/rtgroups": dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.INFO, Gtk.ButtonsType.YES_NO, \ "Kernel thread tunings saved!\n\n" \ "Now you can use it with rtctl:\n\n" \ "rtctl --file %s reset\n\n" \ "If you want the changes to be in " \ "effect every time you boot the system " \ "please move %s to /etc/rtgroups\n\n" \ "Do you want to do that now?" % (filename, filename)) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: filename = "/etc/rtgroups" tuna.generate_rtgroups(filename, kthreads, self.nr_cpus) dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.INFO, Gtk.ButtonsType.OK, \ _("Kernel thread tunings saved to %s!") % filename) dialog.run() dialog.destroy() def on_processlist_button_press_event(self, treeview, event): if event.type != Gdk.EventType.BUTTON_PRESS or event.button != 3: return self.last_x = int(event.x) self.last_y = int(event.y) menu = Gtk.Menu() setattr = Gtk.MenuItem(_("_Set process attributes"), use_underline = True) if self.refreshing: refresh = Gtk.MenuItem(_("Sto_p refreshing the process list"), use_underline = True) else: refresh = Gtk.MenuItem(_("_Refresh the process list"), use_underline = True) if self.show_kthreads: kthreads = Gtk.MenuItem(_("_Hide kernel threads"), use_underline = True) else: kthreads = Gtk.MenuItem(_("_Show kernel threads"), use_underline = True) if self.show_uthreads: uthreads = Gtk.MenuItem(_("_Hide user threads"), use_underline = True) else: uthreads = Gtk.MenuItem(_("_Show user threads"), use_underline = True) help = Gtk.MenuItem(_("_What is this?"), use_underline = True) save_kthreads_tunings = Gtk.MenuItem(_("_Save kthreads tunings"), use_underline = True) menu.add(save_kthreads_tunings) menu.add(setattr) menu.add(refresh) menu.add(kthreads) menu.add(uthreads) menu.add(help) save_kthreads_tunings.connect_object( 'activate', self.save_kthreads_tunings, event) setattr.connect_object('activate', self.edit_attributes, event) refresh.connect_object('activate', self.refresh_toggle, event) kthreads.connect_object('activate', self.kthreads_view_toggled, event) uthreads.connect_object('activate', self.uthreads_view_toggled, event) help.connect_object('activate', self.help_dialog, event) save_kthreads_tunings.show() setattr.show() refresh.show() kthreads.show() uthreads.show() help.show() menu.popup(None, None, None, event, event.button, event.time) def toggle_mask_cpu(self, cpu, enabled): if not enabled: if cpu not in self.cpus_filtered: self.cpus_filtered.append(cpu) self.show(True) else: if cpu in self.cpus_filtered: self.cpus_filtered.remove(cpu) self.show(True) tuna-0.19/tuna/gui/profileview.py000066400000000000000000000365731437350234000170710ustar00rootroot00000000000000import os import shutil import gi from gi.repository import Gtk from tuna import tuna, gui class profileview: def on_loadProfileButton_clicked(self, button): self.dialog = Gtk.FileChooserDialog("Open...", None, \ Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, \ Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) self.dialog.set_default_response(Gtk.ResponseType.OK) filter = Gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") self.dialog.add_filter(filter) self.dialog.set_current_folder(self.config.config["root"]) self.response = self.dialog.run() if self.response == Gtk.ResponseType.OK: self.addFile(self.dialog.get_filename()) self.setProfileFileList() self.dialog.destroy() def setWtree(self, wtree): self.configFileTree = wtree.get_object("profileTree") self.profileContent = wtree.get_object("profileContent") self.configFileCombo = wtree.get_object("profileSelector") self.profileDescription = wtree.get_object("profileDescriptionText") self.frame = wtree.get_object("TunableFramesw") def setProfileFileList(self): self.clearConfig() for val in self.config.populate(): self.addConfig(val) return True def addFile(self, value): try: if os.path.isfile(value): tmp = value.rfind("/") shutil.copy(value, self.config.config['root']+value[tmp:len(value)]) self.setProfileFileList() self.config.load(value[tmp:len(value)]) except Exception as e: self.show_mbox_warning(str(e)) def updateProfileContent(self): try: self.config.cache except: self.config.cache = "" self.profileContentBuffer = self.profileContent.get_buffer() self.profileContentBuffer.set_text(self.config.cache) def clearConfig(self): try: if self.configHandler: self.configs.disconnect(self.configHandler) self.config_store.clear() self.combo_store.clear() self.configHandler = self.configs.connect('cursor_changed', self.changeProfile) except: pass def addConfig(self, config): if not self.configFileTree or not self.configFileCombo: return False try: self.configs self.configFileCombo except AttributeError: self.configHandler = None self.config_store = Gtk.ListStore(str) self.configs = self.configFileTree self.configFileTree.append_column(Gtk.TreeViewColumn('Profile Name', Gtk.CellRendererText(), text=0)) self.configs.set_model(self.config_store) self.combo_store = Gtk.ListStore(str) self.configFileCombo.set_model(self.combo_store) cell = Gtk.CellRendererText() self.configFileCombo.pack_start(cell, True) self.configFileCombo.add_attribute(cell, "text", 0) if self.configHandler: self.configs.disconnect(self.configHandler) self.config_store.append([config]) self.configs.show() self.combo_store.append([config]) self.configHandler = self.configs.connect('cursor_changed', self.changeProfile) self.configFileCombo.show() def changeProfile(self, config): try: f = open(self.config.config['root']+self.config.cacheFileName, 'r') temp = f.read() f.close() self.profileContentBuffer = self.profileContent.get_buffer() buff = self.profileContentBuffer.get_text(self.profileContentBuffer.get_start_iter(), self.profileContentBuffer.get_end_iter(), False) if temp != buff: dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL \ | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, \ "%s\n\n%s\n%s" % \ (_("Config file was changed!"), _("All changes will be lost"), _("Realy continue?"),)) ret = dialog.run() dialog.destroy() if ret == Gtk.ResponseType.NO: old = self.config.cacheFileName.rfind("/") old = self.config.cacheFileName[old+1:len(self.config.cacheFileName)] self.set_current_tree_selection(old) return False except IOError as e: pass currentFile = self.get_current_tree_selection() self.config.fileToCache(currentFile) self.updateProfileContent() self.profileDescription.set_text(self.config.description) def on_SaveButton_clicked(self, widget): try: self.profileContentBuffer = self.profileContent.get_buffer() self.config.cache = self.profileContentBuffer.get_text(self.profileContentBuffer.get_start_iter(), self.profileContentBuffer.get_end_iter(), False) self.config.cacheToFile(self.config.cacheFileName) except IOError as e: self.show_mbox_warning(_("Cannot write to config file: %s") % (self.config.cacheFileName)) def on_UpdateButton_clicked(self, widget): self.profileContentBuffer = self.profileContent.get_buffer() self.temp = self.profileContentBuffer.get_text(self.profileContentBuffer.get_start_iter(), self.profileContentBuffer.get_end_iter(), False) try: if not self.config.loadDirect(self.temp): self.commonview.updateCommonView() self.config.updateDefault(self.config.cacheFileName) self.frame.show() else: self.frame.hide() except RuntimeError as e: self.show_mbox_warning(str(e)) self.frame.hide() def init_default_file(self): self.setProfileFileList() try: if 'lastfile' in self.config.config and \ not self.config.load(self.config.config['lastfile']): cur = self.configFileTree.get_model() for val in cur: if val[0] == self.config.config['lastfile']: self.configFileTree.set_cursor(val.path[0]) self.commonview.updateCommonView() self.frame.show() else: self.frame.hide() except RuntimeError as e: dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, \ _("%s\nRun autocorect?") % _(str(e))) dlgret = dialog.run() dialog.destroy() if dlgret == Gtk.ResponseType.YES: if 'lastfile' in self.config.config: self.config.fixConfigFile(self.config.config['root'] + self.config.config['lastfile']) err = self.config.checkConfigFile(self.config.config['root'] + self.config.config['lastfile']) if err != '': self.show_mbox_warning(_("Default %s" % str(err))) self.frame.hide() else: self.init_default_file() else: self.frame.hide() else: self.frame.hide() def on_profileTree_button_press_event(self, treeview, event): if event.button == 3: x = int(event.x) y = int(event.y) time = event.time pthinfo = treeview.get_path_at_pos(x, y) if pthinfo is not None: path, col, cellx, celly = pthinfo treeview.grab_focus() treeview.set_cursor(path, col, 0) context = Gtk.Menu() item = Gtk.ImageMenuItem(_("New profile")) item.connect("activate", self.on_menu_new) img = Gtk.Image.new_from_stock(Gtk.STOCK_NEW, Gtk.IconSize.MENU) img.show() item.set_image(img) context.append(item) item = Gtk.ImageMenuItem(_("Rename")) item.connect("activate", self.on_menu_rename) img = Gtk.Image.new_from_stock(Gtk.STOCK_FILE, Gtk.IconSize.MENU) img.show() item.set_image(img) context.append(item) item = Gtk.ImageMenuItem(_("Copy")) item.connect("activate", self.on_menu_copy) img = Gtk.Image.new_from_stock(Gtk.STOCK_COPY, Gtk.IconSize.MENU) img.show() item.set_image(img) context.append(item) item = Gtk.ImageMenuItem(_("Delete")) item.connect("activate", self.on_menu_delete) img = Gtk.Image.new_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.MENU) img.show() item.set_image(img) context.append(item) item = Gtk.ImageMenuItem(_("Check")) item.connect("activate", self.on_menu_check) img = Gtk.Image.new_from_stock(Gtk.STOCK_SPELL_CHECK, Gtk.IconSize.MENU) img.show() item.set_image(img) context.append(item) context.show_all() context.popup(None, None, None, event, event.button, time) return True def get_current_tree_selection(self): selection = self.configFileTree.get_selection() tree_model, tree_iter = selection.get_selected() return tree_model.get_value(tree_iter, 0) def set_current_tree_selection(self, string): cur = self.configFileTree.get_model() for val in cur: if val[0] == string: self.configFileTree.set_cursor(val.path[0]) return True return False def on_menu_new(self, widget): filename = self.get_text_dialog(_("Please enter new filename"), \ "empty.conf") if(filename is None or filename == "" or os.path.exists(self.config.config['root']+filename)): self.show_mbox_warning(_("Bad or empty filename %s" % _(filename))) return False try: f = open(self.config.config['root'] + filename, 'w') f.write("#List of enabled categories\n") f.write("[categories]\n") f.write("#format:\n") f.write("# category_identifier=Category Name\n") f.write("\n") f.write("#[category_identifier]\n") f.write("#value.name=default\n") f.write("#value.name=slider_min,slider_max,default\n") f.write("\n") f.write("#[guiAlias]\n") f.write("#value.name=Alias\n") f.write("\n") f.write("#[fileDescription]\n") f.write("#text=Description of this profile\n") f.write("\n") f.close() if self.setProfileFileList(): self.set_current_tree_selection(filename) self.frame.hide() except IOError as io: self.show_mbox_warning(str(io)) return True def on_menu_check(self, widget): filename = self.get_current_tree_selection() err = self.config.checkConfigFile(self.config.config['root']+filename) if err != '': self.show_mbox_warning("%s\n%s" % (_("Config file contain errors:"), _(err))) return False dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, \ Gtk.ButtonsType.OK, "%s\n" % (_("Config file looks OK"))) ret = dialog.run() dialog.destroy() self.set_current_tree_selection(filename) return True def on_menu_rename(self, widget): old_filename = self.get_current_tree_selection() new_filename = self.get_text_dialog(_("Please enter new name for %s" % (old_filename)), old_filename) if(new_filename is None or new_filename == ""): self.show_mbox_warning(_("Bad or empty filename %s" % _(new_filename))) return False try: os.rename(self.config.config['root'] + old_filename, self.config.config['root'] + new_filename) if self.setProfileFileList(): self.set_current_tree_selection(new_filename) if self.config.checkConfigFile(self.config.config['root'] + new_filename) == '': self.commonview.updateCommonView() else: self.frame.hide() except OSError as io: self.show_mbox_warning(str(io)) return True def on_menu_copy(self, widget): old_filename = self.get_current_tree_selection() new_filename = self.get_text_dialog(_("Please enter name for new file"), old_filename) if(new_filename is None or new_filename == ""): self.show_mbox_warning(_("Bad or empty filename %s" % _(new_filename))) return False try: shutil.copy2(self.config.config['root']+old_filename, self.config.config['root']+new_filename) except (shutil.Error, IOError) as e: self.show_mbox_warning(str(e)) if self.setProfileFileList(): self.set_current_tree_selection(new_filename) if self.config.checkConfigFile(self.config.config['root'] + new_filename) == '': self.commonview.updateCommonView() else: self.frame.hide() return True def on_menu_delete(self, widget): filename = self.get_current_tree_selection() dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, \ _("Profile %s will be deleted!\nReally?" % (filename))) ret = dialog.run() dialog.destroy() if ret == Gtk.ResponseType.YES: try: os.unlink(self.config.config['root'] + filename) except OSError as oe: self.show_mbox_warning(str(oe)) return False if self.setProfileFileList(): self.configFileTree.set_cursor(0) currentFile = self.get_current_tree_selection() if self.config.checkConfigFile(self.config.config['root'] + currentFile) == '': self.commonview.updateCommonView() return True self.frame.hide() return False def get_text_dialog(self, message, default=''): d = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, message) entry = Gtk.Entry() entry.set_text(default) entry.show() d.vbox.pack_end(entry, True, True, 0) entry.connect('activate', lambda _: d.response(Gtk.ResponseType.OK)) d.set_default_response(Gtk.ResponseType.OK) r = d.run() text = entry.get_text() d.destroy() if r == Gtk.ResponseType.OK: return text return None def show_mbox_warning(self, message): dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, _((str(message)))) ret = dialog.run() dialog.destroy() tuna-0.19/tuna/gui/util.py000066400000000000000000000102741437350234000155010ustar00rootroot00000000000000import errno import os import gi gi.require_version("Gtk", "3.0") from gi.repository import GObject from gi.repository import Gtk from gi.repository import Pango import tuna.tuna_sched as tuna_sched import procfs from tuna import tuna class list_store_column: def __init__(self, name, type=GObject.TYPE_UINT): self.name = name self.type = type def generate_list_store_columns_with_attr(columns): for column in columns: yield column.type for column in columns: yield GObject.TYPE_UINT def set_store_columns(store, row, new_value): nr_columns = len(new_value) for col in range(nr_columns): col_weight = col + nr_columns cur_value = store.get_value(row, col) if cur_value == new_value[col]: new_weight = Pango.Weight.NORMAL else: new_weight = Pango.Weight.BOLD store.set(row, col, new_value[col], col_weight, new_weight) def on_affinity_text_changed(self): new_affinity_text = self.affinity.get_text().strip() if self.affinity_text != new_affinity_text: try: for cpu in new_affinity_text.strip(",").split(","): new_affinity_cpu_entry = int(cpu, 16) except: try: new_affinity = tuna.cpustring_to_list(new_affinity_text) except: if len(new_affinity_text) > 0 \ and new_affinity_text[-1] != '-' \ and new_affinity_text[0:2] not in ('0x', '0X'): # print "not a hex number" self.affinity.set_text(self.affinity_text) return self.affinity_text = new_affinity_text def invalid_affinity(): dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, \ Gtk.ButtonsType.OK, \ _("Invalid affinity, specify a list of CPUs!")) dialog.run() dialog.destroy() return False def thread_set_attributes(pid_info, new_policy, new_prio, new_affinity, nr_cpus): pid = pid_info.pid changed = False curr_policy = os.sched_getscheduler(pid) curr_prio = int(pid_info["stat"]["rt_priority"]) if new_policy == os.SCHED_OTHER: new_prio = 0 if curr_policy != new_policy or curr_prio != new_prio: param = os.sched_param(new_prio) try: os.sched_setscheduler(pid, new_policy, param) except: dialog = Gtk.MessageDialog(None, \ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ Gtk.MessageType.WARNING, \ Gtk.ButtonsType.OK, \ _("Invalid parameters!")) dialog.run() dialog.destroy() return False curr_policy = os.sched_getscheduler(pid) if curr_policy != new_policy: print(_("couldn't change pid %(pid)d from %(cpol)s(%(cpri)d) to %(npol)s(%(npri)d)!") % \ {'pid': pid, 'cpol': tuna_sched.sched_str(curr_policy), 'cpri': curr_prio, 'npol': tuna_sched.sched_str(new_policy), 'npri': new_prio}) else: changed = True try: curr_affinity = os.sched_getaffinity(pid) except OSError as err: if err.args[0] == errno.ESRCH: return False raise err try: new_affinity = [int(a) for a in new_affinity.split(",")] except: try: new_affinity = tuna.cpustring_to_list(new_affinity) except: new_affinity = procfs.bitmasklist(new_affinity, nr_cpus) new_affinity.sort() if curr_affinity != new_affinity: try: os.sched_setaffinity(pid, new_affinity) except: return invalid_affinity() try: curr_affinity = os.sched_getaffinity(pid) except OSError as err: if err.args[0] == errno.ESRCH: return False raise err if curr_affinity != new_affinity: print(_("couldn't change pid %(pid)d from %(caff)s to %(naff)s!") % \ {'pid':pid, 'caff':curr_affinity, 'naff':new_affinity}) else: changed = True return changed tuna-0.19/tuna/help.py000066400000000000000000002166131437350234000146750ustar00rootroot00000000000000 import gettext _ = gettext.gettext # gettext_noop def N_(s): return s KTHREAD_HELP = { 'kthreadd':N_('Used to create kernel threads via kthread_create(). It is the parent of all the other kernel threads.'), 'posix_cpu_timer':N_('Per-cpu thread that handles POSIX timer callbacks. Timer callbacks are bound to a cpu and are handled by these threads as per-cpu data.'), 'kondemand/':N_('This cpufreq workqueue runs periodically to sample the idleness of the system, increasing or reducing the CPU frequency to save power. \n[One per CPU]'), 'sirq-rcu/':N_('Pushes the RCU grace period along (if possible) and will handle dereferenced RCU callbacks, such as freeing structures after a grace period. \n[One per CPU]'), 'khelper':N_('Used to call user mode helpers from the kernel, such as /sbin/bridge-stp, ocfs2_hb_ctl, pnpbios, poweroff, request-key, etc."'), 'group_balance':N_('Scheduler load balance monitoring.'), 'kjournald':N_('Main thread function used to manage a filesystem logging device journal. This kernel thread is responsible for two things: COMMIT: Every so often we need to commit the current state of the filesystem to disk. The journal thread is responsible for writing all of the metadata buffers to disk. CHECKPOINT: We cannot reuse a used section of the log file until all of the data in that part of the log has been rewritten elsewhere on the disk. Flushing these old buffers to reclaim space in the log is known as checkpointing, and this thread is responsible for that job.'), 'lockd':N_('Locking arbiter for NFS on the system'), 'sirq-sched/':N_('Triggered when a rebalance of tasks is needed to CPU domains. This handles balancing of SCHED_OTHER tasks across CPUs. RT tasks balancing is done directly in schedule and wakeup paths. Runs at prio 1 because it needs to schedule above all SCHED_OTHER tasks. If the user has the same issue but doesn"t mind having latencies against other kernel threads that run here, then its fine. But it should definitely be documented that PRIO 1 has other threads on it at boot up. \n[One per CPU]'), 'sirq-high/':N_('This is from a poor attempt to prioritize tasklets. Some tasklets wanted to run before anything else. Thus there were two tasklet softirqs made. tasklet_vec and tasklet_hi_vec. A driver writer could put their "critical" tasklets into the tasklet_hi_vec and it would run before other softirqs. This never really worked as intended. \n[One per CPU]'), 'kblockd/':N_('Workqueue used to process IO requests. Used by IO schedulers and block device drivers. \n[One per CPU]'), 'sirq-net-rx/':N_('When receiving a packet the device will place the packet on a queue with its hard interrupt (threaded in RT). The sirq-net-rx is responsible for finding out what to do with the packet. It may forward it to another box if the current box is used as a router, or it will find the task the packet is for. If that task is currently waiting for the packet, the softirq might hand it off to that task and the task will handle the rest of the processing of the packet. \n[One per CPU]'), 'krcupreemptd':N_('This should run at the lowest RT priority. With preemptible RCU, a loaded system may have tasks that hold RCU locks but have a high nice value. These tasks may be pushed off for seconds, and if the system is tight on memory, the RCU deferred freeing may not occur. The result can be drastic. The krcupreemptd is a daemon that runs just above SCHED_OTHER and wakes up once a second and performs a synchronize RCU. With RCU boosting, all those that hold RCU locks will inherit the priority of the krcupreemptd and wake up and release the RCU locks. This is only a concern for loaded systems and SCHED_OTHER tasks. If there is an issue of RT tasks starving out SCHED_OTHER tasks and causing problems with freeing memory, then the RT tasks are designed badly.'), 'ksoftirqd/':N_('Activated when under heavy networking activity. Used to avoid monopolizing the CPUs doing just software interrupt processing. \n[One per CPU]'), 'sirq-timer/':N_('Basically the timer wheel. Things that add itself to the timer wheel timeouts will be handled by this softirq. Parts of the kernel that need timeouts will use this softirq (i.e. network timeouts). The resolution to these timeouts are defined by the HZ value. \n[One per CPU]'), 'events/':N_('Global workqueue, used to schedule work to be done in process context. \n[One per CPU]'), 'watchdog/':N_('Run briefly once per second to reset the softlockup timestamp. If this gets delayed for more than 60 seconds then a message will be printed. Use /proc/sys/kernel/hung_task_timeout_secs and /proc/sys/kernel/hung_task_check_count to control this behaviour. Setting /proc/sys/kernel/hung_task_timeout_secs to zero will disable this check. \n[One per CPU]'), 'sirq-net-tx/':N_('This is the network transmit queue. Most of the time the network packets will be handled by the task that is sending the packets out, and doing so at the priority of that task. But if the protocol window or the network device queue is full, then the packets will be pushed off to later. The sirq-net-tx softirq is responsible for sending out these packets. \n[One per CPU]'), 'sirq-block/':N_('Called after a completion to a block device is made. Looking further into this call, I only see a couple of users. The SCSI driver uses this as well as cciss. \n[One per CPU]'), 'sirq-tasklet/':N_('Catch all for those devices that couldn"t use softirqs directly and mostly made before work queues were around. The difference between a tasklet and a softirq is that the same tasklet can not run on two different CPUs at the same time. In this regard it acts like a "task" (hence the name "tasklet"). Various devices use tasklets. \n[One per CPU]'), 'usb-storage':N_('Per USB storage device virtual SCSI controller. Persistant across device insertion/removal, as is the SCSI node. This is done so that a device which is removed can be re-attached and be granted the same /dev node as before, creating persistance between connections of the target unit. Gets commands from the SCSI mid-layer and, after sanity checking several things, sends the command to the "protocol" handler. This handler is responsible for re-writing the command (if necessary) into a form which the device will accept. For example, ATAPI devices do not support 6-byte commands. Thus, they must be re-written into 10-byte variants.'), 'migration/':N_('High priority system thread that performs thread migration by bumping thread off CPU then pushing onto another runqueue. \n[One per CPU]'), 'rpciod/':N_('Handles Sun RPC network messages (mainly for NFS) \n[One per CPU]') } PROC_SYS_HELP = { 'net.core.bpf_jit_enable':N_('This enables Berkeley Packet Filter Just in Time compiler. Currently supported on x86_64 architecture, bpf_jit provides a framework to speed packet filtering, the one used by tcpdump/libpcap for example.Values:\n - 0 -disable the JIT (default value)\n - 1 - enable the JIT\n - 2 - enable the JIT and ask the compiler to emit traces on kernel log.'), 'net.core.dev_weight':N_('The maximum number of packets that kernel can handle on a NAPI interrupt, it\'s a Per-CPU variable.\nDefault: 64'), 'net.core.message_burst':N_('This parameter are used to limit the warning messages written to the kernel log from the networking code. They enforce a rate limit to make a denial-of-service attack impossible. Message_burst controls when messages will be dropped. The default settings limit warning messages to one every five seconds.'), 'net.core.message_cost':N_('This parameter are used to limit the warning messages written to the kernel log from the networking code. They enforce a rate limit to make a denial-of-service attack impossible. A higher message_cost factor, results in fewer messages that will be written. The default settings limit warning messages to one every five seconds.'), 'net.core.netdev_budget':N_('Maximum number of packets taken from all interfaces in one polling cycle (NAPI poll). In one polling cycle interfaces which are registered to polling are probed in a round-robin manner. The limit of packets in one such probe can be set per-device via sysfs class/net//weight.'), 'net.core.netdev_max_backlog':N_('Maximum number of packets, queued on the INPUT side, when the interface receives packets faster than kernel can process them.'), 'net.core.netdev_tstamp_prequeue':N_('If set to 0, RX packet timestamps can be sampled after RPS processing, when the target CPU processes packets. It might give some delay on timestamps, but permit to distribute the load on several cpus.\nIf set to 1 (default), timestamps are sampled as soon as possible, before queueing.'), 'net.core.optmem_max':N_('Maximum ancillary buffer size allowed per socket. Ancillary data is a sequence of struct cmsghdr structures with appended data.'), 'net.core.rmem_default':N_('The default setting of the socket receive buffer in bytes.'), 'net.core.rmem_max':N_('The maximum receive socket buffer size in bytes.'), 'net.core.rps_sock_flow_entries':N_('This controls the maximum number of sockets/flows that the kernel can steer towards any specified CPU. This is a system-wide, shared limit.'), 'net.core.somaxconn':N_('Limit of socket listen() backlog, known in userspace as SOMAXCONN. See also tcp_max_syn_backlog for additional tuning for TCP sockets.\Default: 128.'), 'net.core.warnings':N_('This controls console messages from the networking stack that can occur because of problems on the network like duplicate address or bad checksums. Normally, this should be enabled, but if the problem persists the messages can be disabled.'), 'net.core.wmem_default':N_('The default setting (in bytes) of the socket send buffer.'), 'net.core.wmem_max':N_('The maximum send socket buffer size in bytes.'), 'net.core.xfrm_acq_expires':N_('Hard timeout in seconds for acquire requests'), #'net.core.xfrm_aevent_etime':N_(''), #'net.core.xfrm_aevent_rseqth':N_(''), #'net.core.xfrm_larval_drop':N_(''), 'net.ipv4.cipso_cache_bucket_size':N_('The CIPSO label cache consists of a fixed size hash table with each hash bucket containing a number of cache entries. This variable limits the number of entries in each hash bucket; the larger the value the more CIPSO label mappings that can be cached. When the number of entries in a given hash bucket reaches this limit adding new entries causes the oldest entry in the bucket to be removed to make room.\nDefault: 10'), 'net.ipv4.cipso_cache_enable':N_('If set, enable additions to and lookups from the CIPSO label mapping cache. If unset, additions are ignored and lookups always result in a miss. However, regardless of the setting the cache is still invalidated when required when means you can safely toggle this on and off and the cache will always be "safe".\nDefault: 1'), 'net.ipv4.cipso_rbm_optfmt':N_('Enable the "Optimized Tag 1 Format" This means that when set the CIPSO tag will be padded with empty categories in order to make the packet data 32-bit aligned.\nDefault: 0'), 'net.ipv4.cipso_rbm_strictvalid':N_('If set, do a very strict check of the CIPSO option when ip_options_compile() is called. If unset, relax the checks done during ip_options_compile(). Either way is "safe" as errors are caught else where in the CIPSO processing code but setting this to 0 (False) should result in less work (i.e. it should be faster) but could cause problems with other implementations that require strict checking.\nDefault: 0'), 'net.ipv4.icmp_echo_ignore_all':N_('If set non-zero, then the kernel will ignore all ICMP ECHO requests sent to it.\nDefault: 0'), 'net.ipv4.icmp_echo_ignore_broadcasts':N_('If set non-zero, then the kernel will ignore all ICMP ECHO and TIMESTAMP requests sent to it via broadcast/multicast.\nDefault: 1'), 'net.ipv4.icmp_errors_use_inbound_ifaddr':N_('If zero, icmp error messages are sent with the primary address of the exiting interface.\nIf non-zero, the message will be sent with the primary address of the interface that received the packet that caused the icmp error. This is the behaviour network many administrators will expect from a router. And it can make debugging complicated network layouts much easier. Note that if no primary address exists for the interface selected, then the primary address of the first non-loopback interface that has one will be used regardless of this setting.\nDefault: 0'), 'net.ipv4.icmp_ignore_bogus_error_responses':N_('Some routers violate RFC1122 by sending bogus responses to broadcast frames. Such violations are normally logged via a kernel warning. If this is set to TRUE, the kernel will not give such warnings, which will avoid log file clutter.\nDefault: FALSE'), 'net.ipv4.icmp_ratelimit':N_('Limit the maximal rates for sending ICMP packets whose type matches icmp_ratemask (see below) to specific targets. 0 to disable any limiting, otherwise the minimal space between responses in milliseconds.\nDefault: 1000'), 'net.ipv4.icmp_ratemask':N_('Mask made of ICMP types for which rates are being limited.\n - Significant bits: IHGFEDCBA9876543210\n - Default mask: 0000001100000011000 (6168)\n - - 0 Echo Reply\n - - 3 Destination Unreachable *\n - - 4 Source Quench *\n - - 5 Redirect\n - - 8 Echo Request\n - - B Time Exceeded *\n - - C Parameter Problem *\n - - D Timestamp Request\n - - E Timestamp Reply\n - - F Info Request\n - - G Info Reply\n - - H Address Mask Request\n - - I Address Mask Reply\nDefault: values rate by *'), 'net.ipv4.igmp_max_memberships':N_('Change the maximum number of multicast groups we can subscribe to.\nDefault: 20'), #'net.ipv4.igmp_max_msf':N_(''), 'net.ipv4.inet_peer_maxttl':N_('Maximum time-to-live of entries. Unused entries will expire after this period of time if there is no memory pressure on the pool (i.e. when the number of entries in the pool is very small).\nUnit: second'), 'net.ipv4.inet_peer_minttl':N_('Minimum time-to-live of entries. Should be enough to cover fragment time-to-live on the reassembling side. This minimum time-to-live is guaranteed if the pool size is less than inet_peer_threshold.\nUnit: second'), 'net.ipv4.inet_peer_threshold':N_('The approximate size of the storage. Starting from this threshold entries will be thrown aggressively. This threshold also determines entries time-to-live and time intervals between garbage collection passes. More entries, less time-to-live, less GC interval.'), 'net.ipv4.ip_default_ttl':N_('Default value of TTL field (Time To Live) for outgoing (but not forwarded) IP packets. Should be between 1 and 255 inclusive.\nDefault: 64 (as recommended by RFC1700)'), 'net.ipv4.ip_dynaddr':N_('If set non-zero, enables support for dynamic addresses. If set to a non-zero value larger than 1, a kernel log message will be printed when dynamic address rewriting occurs.\nDefault: 0'), #'net.ipv4.ip_early_demux':N_(''), 'net.ipv4.ip_forward':N_('Forward Packets between interfaces.\nO - disabled (default)\nnot 0 - enabled'), 'net.ipv4.ipfrag_high_thresh':N_('Maximum memory used to reassemble IP fragments. When ipfrag_high_thresh bytes of memory is allocated for this purpose, the fragment handler will toss packets until ipfrag_low_thresh is reached.'), 'net.ipv4.ipfrag_low_thresh':N_('Minimum memory used to reassemble IP fragments. When ipfrag_high_thresh bytes of memory is allocated for this purpose, the fragment handler will toss packets until ipfrag_low_thresh is reached.'), 'net.ipv4.ipfrag_max_dist':N_('Ipfrag_max_dist is a non-negative integer value which defines the maximum "disorder" which is allowed among fragments which share a common IP source address. Note that reordering of packets is not unusual, but if a large number of fragments arrive from a source IP address while a particular fragment queue remains incomplete, it probably indicates that one or more fragments belonging to that queue have been lost. When ipfrag_max_dist is positive, an additional check is done on fragments before they are added to a reassembly queue - if ipfrag_max_dist (or more) fragments have arrived from a particular IP address between additions to any IP fragment queue using that source address, it\'s presumed that one or more fragments in the queue are lost. The existing fragment queue will be dropped, and a new one started. An ipfrag_max_dist value of zero disables this check.\n\nUsing a very small value, e.g. 1 or 2, for ipfrag_max_dist can result in unnecessarily dropping fragment queues when normal reordering of packets occurs, which could lead to poor application performance. Using a very large value, e.g. 50000, increases the likelihood of incorrectly reassembling IP fragments that originate from different IP datagrams, which could result in data corruption.\nDefault: 64'), 'net.ipv4.ipfrag_secret_interval':N_('Regeneration interval of the hash secret (or lifetime for the hash secret) for IP fragments.\nDefault: 600\nUnit: second'), 'net.ipv4.ipfrag_time':N_('Time to keep an IP fragment in memory.\nUnit: second'), 'net.ipv4.ip_local_port_range':N_('Defines the local port range that is used by TCP and UDP to choose the local port. The first number is the first, the second the last local port number.\nDefault value depends on amount of memory available on the system:\n - > 128Mb 32768-61000\n - < 128Mb 1024-4999 or even less.\nThis number defines number of active connections, which this system can issue simultaneously to systems not supporting TCP extensions (timestamps). With tcp_tw_recycle enabled (i.e. by default) range 1024-4999 is enough to issue up to 2000 connections per second to systems supporting timestamps.'), 'net.ipv4.ip_local_reserved_ports':N_('Specify the ports which are reserved for known third-party applications. These ports will not be used by automatic port assignments (e.g. when calling connect() or bind() with port number 0). Explicit port allocation behavior is unchanged.\nThe format used for both input and output is a comma separated list of ranges (e.g. "1,2-4,10-10" for ports 1, 2, 3, 4 and 10). Writing to the file will clear all previously reserved ports and update the current list with the one given in the input.\nDefault: Empty'), 'net.ipv4.ip_nonlocal_bind':N_('If set, allows processes to bind() to non-local IP addresses, which can be quite useful - but may break some applications.\nDefault: 0'), 'net.ipv4.ip_no_pmtu_disc':N_('Disable Path MTU Discovery.\nDefault FALSE'), #'net.ipv4.ping_group_range':N_(''), 'net.ipv4.tcp_abc':N_('Controls Appropriate Byte Count (ABC) defined in RFC3465. ABC is a way of increasing congestion window (cwnd) more slowly in response to partial acknowledgments.\nPossible values are:\n - 0 increase cwnd once per acknowledgment (no ABC)\n - 1 increase cwnd once per acknowledgment of full sized segment\n - 2 allow increase cwnd by two if acknowledgment is of two segments to compensate for delayed acknowledgments.\nDefault: 0 (off)'), 'net.ipv4.tcp_abort_on_overflow':N_('If listening service is too slow to accept new connections, reset them. Default state is FALSE. It means that if overflow occurred due to a burst, connection will recover. Enable this option _only_ if you are really sure that listening daemon cannot be tuned to accept connections faster. Enabling this option can harm clients of your server.\nDefault: FALSE.'), 'net.ipv4.tcp_adv_win_scale':N_('Count buffering overhead as bytes/2^tcp_adv_win_scale (if tcp_adv_win_scale > 0) or bytes-bytes/2^(-tcp_adv_win_scale), if it is <= 0. Possible values are [-31, 31], inclusive.\nDefault: 2'), 'net.ipv4.tcp_allowed_congestion_control':N_('Show/set the congestion control choices available to non-privileged processes. The list is a subset of those listed in tcp_available_congestion_control. Default is "reno" and the default setting (tcp_congestion_control).'), 'net.ipv4.tcp_app_win':N_('Reserve max(window/2^tcp_app_win, mss) of window for application buffer. Value 0 is special, it means that nothing is reserved.\nDefault: 31'), 'net.ipv4.tcp_available_congestion_control':N_('Shows the available congestion control choices that are registered. More congestion control algorithms may be available as modules, but not loaded.'), 'net.ipv4.tcp_base_mss':N_('The initial value of search_low to be used by the packetization layer Path MTU discovery (MTU probing). If MTU probing is enabled, this is the initial MSS used by the connection.'), 'net.ipv4.tcp_congestion_control':N_('Set the congestion control algorithm to be used for new connections. The algorithm "reno" is always available, but additional choices may be available based on kernel configuration. Default is set as part of kernel configuration.'), 'net.ipv4.tcp_cookie_size':N_('Default size of TCP Cookie Transactions (TCPCT) option, that may be overridden on a per socket basis by the TCPCT socket option. Values greater than the maximum (16) are interpreted as the maximum. Values greater than zero and less than the minimum (8) are interpreted as the minimum. Odd values are interpreted as the next even value.\nDefault: 0 (off).'), 'net.ipv4.tcp_dma_copybreak':N_('Lower limit, in bytes, of the size of socket reads that will be offloaded to a DMA copy engine, if one is present in the system and CONFIG_NET_DMA is enabled.\nDefault: 4096'), 'net.ipv4.tcp_dsack':N_('Allows TCP to send "duplicate" SACKs.'), #'net.ipv4.tcp_early_retrans':N_(''), 'net.ipv4.tcp_ecn':N_('Enable Explicit Congestion Notification (ECN) in TCP. ECN is only used when both ends of the TCP flow support it. It is useful to avoid losses due to congestion (when the bottleneck router supports ECN).\nPossible values are:\n - 0 disable ECN\n - 1 ECN enabled\n - 2 Only server-side ECN enabled. If the other end doesnot support ECN, behavior is like with ECN disabled.\nDefault: 2'), 'net.ipv4.tcp_fack':N_('Enable FACK congestion avoidance and fast retransmission. The value is not used, if tcp_sack is not enabled.'), #'net.ipv4.tcp_fastopen':N_(''), #'net.ipv4.tcp_fastopen_key':N_(''), 'net.ipv4.tcp_fin_timeout':N_('Time to hold socket in state FIN-WAIT-2, if it was closed by our side. Peer can be broken and never close its side, or even died unexpectedly. Usual value used in 2.2 was 180 seconds, you may restore it, but remember that if your machine is even underloaded WEB server, you risk to overflow memory with kilotons of dead sockets, FIN-WAIT-2 sockets are less dangerous than FIN-WAIT-1, because they eat maximum 1.5K of memory, but they tend to live longer.\nDefault: 60\nUnit: second'), 'net.ipv4.tcp_frto':N_('Enables Forward RTO-Recovery (F-RTO) defined in RFC4138. F-RTO is an enhanced recovery algorithm for TCP retransmission timeouts. It is particularly beneficial in wireless environments where packet loss is typically due to random radio interference rather than intermediate router congestion. F-RTO is sender-side only modification. Therefore it does not require any support from the peer.\n If set to 1, basic version is enabled. 2 enables SACK enhanced F-RTO if flow uses SACK. The basic version can be used also when SACK is in use though scenario(s) with it exists where F-RTO interacts badly with the packet counting of the SACK enabled TCP flow.'), 'net.ipv4.tcp_frto_response':N_('When F-RTO has detected that a TCP retransmission timeout was spurious (i.e, the timeout would have been avoided had TCP set a longer retransmission timeout), TCP has several options what to do next.\nPossible values are:\n - 0 Rate halving based; a smooth and conservative response, results in halved cwnd and ssthresh after one RTT\n - 1 Very conservative response; not recommended because even though being valid, it interacts poorly with the rest of Linux TCP, halves cwnd and ssthresh immediately\n - 2 Aggressive response; undoes congestion control measures that are now known to be unnecessary (ignoring the possibility of a lost retransmission that would require TCP to be more cautious), cwnd and ssthresh are restored to the values prior timeout\nDefault: 0 (rate halving based)'), #'net.ipv4.tcp_challenge_ack_limit':N_(''), 'net.ipv4.tcp_keepalive_intvl':N_('How frequently the probes are send out. Multiplied by tcp_keepalive_probes it is time to kill not responding connection, after probes started.\nDefault: 75\nUnit: second'), 'net.ipv4.tcp_keepalive_probes':N_('How many keepalive probes TCP sends out, until it decides that the connection is broken.\nDefault: 9'), 'net.ipv4.tcp_keepalive_time':N_('How often TCP sends out keepalive messages when keepalive is enabled.\nDefault: 7200.\nUnit: second'), #'net.ipv4.tcp_limit_output_bytes':N_(''), 'net.ipv4.tcp_low_latency':N_('If set, the TCP stack makes decisions that prefer lower latency as opposed to higher throughput. By default, this option is not set meaning that higher throughput is preferred. An example of an application where this default should be changed would be a Beowulf compute cluster.\nDefault: 0'), 'net.ipv4.tcp_max_orphans':N_('Maximal number of TCP sockets not attached to any user file handle, held by system. If this number is exceeded orphaned connections are reset immediately and warning is printed. This limit exists only to prevent simple DoS attacks, you _must_ not rely on this or lower the limit artificially, but rather increase it (probably, after increasing installed memory), if network conditions require more than default value, and tune network services to linger and kill such states more aggressively. Let me to remind again: each orphan eats up to ~64K of unswappable memory.'), 'net.ipv4.tcp_max_ssthresh':N_('Limited Slow-Start for TCP with large congestion windows (cwnd) defined in RFC3742. Limited slow-start is a mechanism to limit growth of the cwnd on the region where cwnd is larger than tcp_max_ssthresh. TCP increases cwnd by at most tcp_max_ssthresh segments, and by at least tcp_max_ssthresh/2 segments per RTT when the cwnd is above tcp_max_ssthresh. If TCP connection increased cwnd to thousands (or tens of thousands) segments, and thousands of packets were being dropped during slow-start, you can set tcp_max_ssthresh to improve performance for new TCP connection.\nDefault: 0 (off)'), 'net.ipv4.tcp_max_syn_backlog':N_('Maximal number of remembered connection requests, which are still did not receive an acknowledgment from connecting client. Default value is 1024 for systems with more than 128Mb of memory, and 128 for low memory machines. If server suffers of overload, try to increase this number.'), 'net.ipv4.tcp_max_tw_buckets':N_('Maximal number of timewait sockets held by system simultaneously. If this number is exceeded time-wait socket is immediately destroyed and warning is printed. This limit exists only to prevent simple DoS attacks, you _must_ not lower the limit artificially, but rather increase it (probably, after increasing installed memory), if network conditions require more than default value.'), 'net.ipv4.tcp_mem':N_('Vector of 3 values:: min, pressure, max\n - min: below this number of pages TCP is not bothered about its memory appetite.\n - pressure: when amount of memory allocated by TCP exceeds this number of pages, TCP moderates its memory consumption and enters memory pressure mode, which is exited when memory consumption falls under "min".\n - max: number of pages allowed for queueing by all TCP sockets.\nDefaults are calculated at boot time from amount of available memory.'), 'net.ipv4.tcp_moderate_rcvbuf':N_('If set, TCP performs receive buffer auto-tuning, attempting to automatically size the buffer (no greater than tcp_rmem[2]) to match the size required by the path for full throughput.\nDefault: 1'), 'net.ipv4.tcp_mtu_probing':N_('Controls TCP Packetization-Layer Path MTU Discovery. \nTakes three values:\n0 - Disabled\n1 - Disabled by default, enabled when an ICMP black hole detected\n2 - Always enabled, use initial MSS of tcp_base_mss.'), 'net.ipv4.tcp_no_metrics_save':N_('By default, TCP saves various connection metrics in the route cache when the connection closes, so that connections established in the near future can use these to set initial conditions. Usually, this increases overall performance, but may sometimes cause performance degradation. If set, TCP will not cache metrics on closing connections.'), 'net.ipv4.tcp_orphan_retries':N_('This value influences the timeout of a locally closed TCP connection, when RTO retransmissions remain unacknowledged.\nIf your machine is a loaded WEB server, you should think about lowering this value, such sockets may consume significant resources.\nDefault: 8'), 'net.ipv4.tcp_reordering':N_('Maximal reordering of packets in a TCP stream.\nDefault: 3'), 'net.ipv4.tcp_retrans_collapse':N_('Bug-to-bug compatibility with some broken printers. On retransmit try to send bigger packets to work around bugs in certain TCP stacks.'), 'net.ipv4.tcp_retries1':N_('This value influences the time, after which TCP decides, that something is wrong due to unacknowledged RTO retransmissions, and reports this suspicion to the network layer.\nRFC 1122 recommends at least 3 retransmissions, which is the default.\nDefault: 3'), 'net.ipv4.tcp_retries2':N_('This value influences the timeout of an alive TCP connection, when RTO retransmissions remain unacknowledged. Given a value of N, a hypothetical TCP connection following exponential backoff with an initial RTO of TCP_RTO_MIN would retransmit N times before killing the connection at the (N+1)th RTO. The default value of 15 yields a hypothetical timeout of 924.6 seconds and is a lower bound for the effective timeout. TCP will effectively time out at the first RTO which exceeds the hypothetical timeout. RFC 1122 recommends at least 100 seconds for the timeout, which corresponds to a value of at least 8.\Default: 8'), 'net.ipv4.tcp_rfc1337':N_('If set, the TCP stack behaves conforming to RFC1337. If unset, we are not conforming to RFC, but prevent TCP TIME_WAIT assassination.\nDefault: 0'), 'net.ipv4.tcp_rmem':N_('Vector of 3 values: min, default, max\n - min: Minimal size of receive buffer used by TCP sockets. It is guaranteed to each TCP socket, even under moderate memory pressure.\nDefault: 8K\n - default: initial size of receive buffer used by TCP sockets. This value overrides net.core.rmem_default used by other protocols. Default: 87380 bytes. This value results in window of 65535 with default setting of tcp_adv_win_scale and tcp_app_win:0 and a bit less for default tcp_app_win. See below about these variables. - max: maximal size of receive buffer allowed for automatically selected receiver buffers for TCP socket. This value does not override net.core.rmem_max. Calling setsockopt() with SO_RCVBUF disables automatic tuning of that socket\'s receive buffer size, in which case this value is ignored.\nDefault: between 87380B and 4MB, depending on RAM size.'), 'net.ipv4.tcp_sack':N_('Enable select acknowledgments (SACKS).'), 'net.ipv4.tcp_slow_start_after_idle':N_('If set, provide RFC2861 behavior and time out the congestion window after an idle period. An idle period is defined at the current RTO. If unset, the congestion window will not be timed out after an idle period.\nDefault: 1'), 'net.ipv4.tcp_stdurg':N_('Use the Host requirements interpretation of the TCP urgent pointer field. Most hosts use the older BSD interpretation, so if you turn this on Linux might not communicate correctly with them.\nDefault: FALSE'), 'net.ipv4.tcp_synack_retries':N_('Number of times SYNACKs for a passive TCP connection attempt will be retransmitted. Should not be higher than 255.\nDefault: 5, which corresponds to ~180seconds.'), 'net.ipv4.tcp_syncookies':N_('Only valid when the kernel was compiled with CONFIG_SYNCOOKIES\nSend out syncookies when the syn backlog queue of a socket overflows. This is to prevent against the common "SYN flood attack"\nDefault: FALSE'), 'net.ipv4.tcp_syn_retries':N_('Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 255.\nDefault: 5, which corresponds to ~180seconds.'), 'net.ipv4.tcp_thin_dupack':N_('Enable dynamic triggering of retransmissions after one dupACK for thin streams. If set, a check is performed upon reception of a dupACK to determine if the stream is thin (less than 4 packets in flight). As long as the stream is found to be thin, data is retransmitted on the first received dupACK. This improves retransmission latency for non-aggressive thin streams, often found to be time-dependent.\nDefault: 0'), 'net.ipv4.tcp_thin_linear_timeouts':N_('Enable dynamic triggering of linear timeouts for thin streams. If set, a check is performed upon retransmission by timeout to determine if the stream is thin (less than 4 packets in flight). As long as the stream is found to be thin, up to 6 linear timeouts may be performed before exponential backoff mode is initiated. This improves retransmission latency for non-aggressive thin streams, often found to be time-dependent.\nDefault: 0'), 'net.ipv4.tcp_timestamps':N_('Enable timestamps as defined in RFC1323.'), 'net.ipv4.tcp_tso_win_divisor':N_('This allows control over what percentage of the congestion window can be consumed by a single TSO frame. The setting of this parameter is a choice between burstiness and building larger TSO frames.\nDefault: 3'), 'net.ipv4.tcp_tw_recycle':N_('Enable fast recycling TIME-WAIT sockets. It should not be changed without advice/request of technical experts.\nDefault: 0'), 'net.ipv4.tcp_tw_reuse':N_('Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. It should not be changed without advice/request of technical experts.\nDefault: 0'), 'net.ipv4.tcp_window_scaling':N_('Enable window scaling as defined in RFC1323.'), 'net.ipv4.tcp_wmem':N_('Vector of 3 values: min, default, max\n - min: Amount of memory reserved for send buffers for TCP sockets. Each TCP socket has rights to use it due to fact of its birth.\nDefault: 4K\n - default: initial size of send buffer used by TCP sockets. This value overrides net.core.wmem_default used by other protocols. It is usually lower than net.core.wmem_default.\nDefault: 16K\n - max: Maximal amount of memory allowed for automatically tuned send buffers for TCP sockets. This value does not override net.core.wmem_max. Calling setsockopt() with SO_SNDBUF disables automatic tuning of that socket\'s send buffer size, in which case this value is ignored.\nDefault: between 64K and 4MB, depending on RAM size.'), 'net.ipv4.tcp_workaround_signed_windows':N_('If set, assume no receipt of a window scaling option means the remote TCP is broken and treats the window as a signed quantity. If unset, assume the remote TCP is not broken even if we do not receive a window scaling option from them.\nDefault: 0'), 'net.ipv4.udp_mem':N_('Vector of 3 values: min, pressure, max\nNumber of pages allowed for queueing by all UDP sockets.\n - min: Below this number of pages UDP is not bothered about its memory appetite. When amount of memory allocated by UDP exceeds this number, UDP starts to moderate memory usage.\n - pressure: This value was introduced to follow format of tcp_mem.\n - max: Number of pages allowed for queueing by all UDP sockets.\nDefault: calculated at boot time from amount of available memory.'), 'net.ipv4.udp_rmem_min':N_('Minimal size of receive buffer used by UDP sockets in moderation. Each UDP socket is able to use the size for receiving data, even if total pages of UDP sockets exceed udp_mem pressure.\nDefault: 4096\nUnit: byte'), 'net.ipv4.udp_wmem_min':N_('Minimal size of send buffer used by UDP sockets in moderation. Each UDP socket is able to use the size for sending data, even if total pages of UDP sockets exceed udp_mem pressure.\nDefault: 4096\nUnit: byte'), #'net.ipv4.xfrm4_gc_thresh':N_(''), #'net.ipv4.route.error_burst':N_(''), #'net.ipv4.route.error_cost':N_(''), #'net.ipv4.route.flush':N_(''), #'net.ipv4.route.gc_elasticity':N_(''), #'net.ipv4.route.gc_interval':N_(''), #'net.ipv4.route.gc_min_interval':N_(''), #'net.ipv4.route.gc_min_interval_ms':N_(''), #'net.ipv4.route.gc_thresh':N_(''), #'net.ipv4.route.gc_timeout':N_(''), 'net.ipv4.route.max_size':N_('Maximum number of routes allowed in the kernel. Increase this when using large numbers of interfaces and/or routes.'), #'net.ipv4.route.min_adv_mss':N_(''), #'net.ipv4.route.min_pmtu':N_(''), #'net.ipv4.route.mtu_expires':N_(''), #'net.ipv4.route.redirect_load':N_(''), #'net.ipv4.route.redirect_number':N_(''), #'net.ipv4.route.redirect_silence':N_(''), #'net.ipv4.*.anycast_delay':N_(''), 'net.ipv4.*.app_solicit':N_('The maximum number of probes to send to the user space ARP daemon via netlink before dropping back to multicast probes.\nDefault: 0'), #'net.ipv4.*.base_reachable_time':N_(''), #'net.ipv4.*.base_reachable_time_ms':N_(''), #'net.ipv4.*.delay_first_probe_time':N_(''), #'net.ipv4.*.gc_interval':N_(''), #'net.ipv4.*.gc_stale_time':N_(''), #'net.ipv4.*.gc_thresh1':N_(''), #'net.ipv4.*.gc_thresh2':N_(''), #'net.ipv4.*.gc_thresh3':N_(''), #'net.ipv4.*.locktime':N_(''), #'net.ipv4.*.mcast_solicit':N_(''), #'net.ipv4.*.proxy_delay':N_(''), #'net.ipv4.*.proxy_qlen':N_(''), #'net.ipv4.*.retrans_time':N_(''), #'net.ipv4.*.retrans_time_ms':N_(''), #'net.ipv4.*.ucast_solicit':N_(''), #'net.ipv4.*.unres_qlen':N_(''), #'net.ipv4.*.unres_qlen_bytes':N_(''), #'net.ipv6.bindv6only':N_(''), #'net.ipv6.*.accept_dad':N_(''), #'net.ipv6.*.accept_ra':N_(''), #'net.ipv6.*.accept_ra_defrtr':N_(''), #'net.ipv6.*.accept_ra_pinfo':N_(''), #'net.ipv6.*.accept_ra_rt_info_max_plen':N_(''), #'net.ipv6.*.accept_ra_rtr_pref':N_(''), #'net.ipv6.*.accept_redirects':N_(''), #'net.ipv6.*.accept_source_route':N_(''), #'net.ipv6.*.autoconf':N_(''), #'net.ipv6.*.dad_transmits':N_(''), #'net.ipv6.*.disable_ipv6':N_(''), #'net.ipv6.*.force_mld_version':N_(''), #'net.ipv6.*.force_tllao':N_(''), #'net.ipv6.*.forwarding':N_(''), #'net.ipv6.*.hop_limit':N_(''), #'net.ipv6.*.max_addresses':N_(''), #'net.ipv6.*.max_desync_factor':N_(''), #'net.ipv6.*.mc_forwarding':N_(''), #'net.ipv6.*.mtu':N_(''), #'net.ipv6.*.optimistic_dad':N_(''), #'net.ipv6.*.proxy_ndp':N_(''), #'net.ipv6.*.regen_max_retry':N_(''), #'net.ipv6.*.router_probe_interval':N_(''), #'net.ipv6.*.router_solicitation_delay':N_(''), #'net.ipv6.*.router_solicitation_interval':N_(''), #'net.ipv6.*.router_solicitations':N_(''), #'net.ipv6.*.temp_prefered_lft':N_(''), #'net.ipv6.*.temp_valid_lft':N_(''), #'net.ipv6.*.use_tempaddr':N_(''), #'net.ipv6.icmp.ratelimit':N_(''), #'net.ipv6.ip6frag_high_thresh':N_(''), #'net.ipv6.ip6frag_low_thresh':N_(''), #'net.ipv6.ip6frag_secret_interval':N_(''), #'net.ipv6.ip6frag_time':N_(''), #'net.ipv6.mld_max_msf':N_(''), #'net.ipv6.route.flush':N_(''), #'net.ipv6.route.gc_elasticity':N_(''), #'net.ipv6.route.gc_interval':N_(''), #'net.ipv6.route.gc_min_interval':N_(''), #'net.ipv6.route.gc_min_interval_ms':N_(''), #'net.ipv6.route.gc_thresh':N_(''), #'net.ipv6.route.gc_timeout':N_(''), #'net.ipv6.route.max_size':N_(''), #'net.ipv6.route.min_adv_mss':N_(''), #'net.ipv6.route.mtu_expires':N_(''), #'net.ipv6.xfrm6_gc_thresh':N_(''), 'kernel.acct':N_('Highwater lowwater frequency\n\nIf BSD-style process accounting is enabled these values control its behaviour. If free space on filesystem where the log lives goes below % accounting suspends. If free space gets above % accounting resumes. determines how often do we check the amount of free space (value is in seconds). \nDefault: 4 2 30\nThat is, suspend accounting if there left <= 2% free; resume it if we got >=4%; consider information about amount of free space valid for 30 seconds.'), 'kernel.acpi_video_flags':N_('This allows mode of video boot to be set during run time.'), 'kernel.auto_msgmni':N_('Enables/Disables automatic recomputing of msgmni upon memory add/remove or upon ipc namespace creation/removal (see the msgmni description above). Set this to "1" for enable msgmni automatic recomputing or "0" to disable auto_msgmni. Default value is 1.'), #'kernel.blk_iopoll':N_(''), 'kernel.bootloader_type':N_('x86 bootloader identification\n\nThis gives the bootloader type number as indicated by the bootloader,shifted left by 4, and OR with the low four bits of the bootloader version. The reason for this encoding is that this used to match the type_of_loader field in the kernel header; the encoding is kept for backwards compatibility. That is, if the full bootloader type number is 0x15 and the full version number is 0x234, this file will contain the value 340 = 0x154.'), 'kernel.bootloader_version':N_('x86 bootloader version\n\nThe complete bootloader version number. In the example above, this file will contain the value 564 = 0x234.'), 'kernel.callhome':N_('Controls the kernel\'s callhome behavior in case of a kernel panic.\n\nThe s390 hardware allows an operating system to send a notification to a service organization (callhome) in case of an operating system panic.\n\nWhen the value in this file is 0 (which is the default behavior) nothing happens in case of a kernel panic. If this value is set to "1" the complete kernel oops message is send to the IBM customer service organization in case the mainframe the Linux operating system is running on has a service contract with IBM.'), #'kernel.compat-log':N_(''), 'kernel.core_pattern':N_('core_pattern is used to specify a core dumpfile pattern name.\nMax length 128 characters; default value is "core"\nCore_pattern is used as a pattern template for the output filename; certain string patterns (beginning with \'%\') are substituted with their actual values. backward compatibility with core_uses_pid: If core_pattern does not include "%p" (default does not) and core_uses_pid is set, then .PID will be appended to the filename.Corename format specifiers:\n - % - \'%\' is dropped\n - %% - output one \'%\'\n - %p - pid\n - %u - uid\n - %g - gid\n - %s - signal number\n - %t - UNIX time of dump\n - %h - hostname\n - %e - executable filename (may be shortened)\n - %E - executable path\n - % both are dropped\nIf the first character of the pattern is a \'|\', the kernel will treat the rest of the pattern as a command to run. The core dump will be written to the standard input of that program instead of to a file.'), 'kernel.core_pipe_limit':N_('This is only applicable when core_pattern is configured to pipe core files to a user space helper (when the first character of core_pattern is a \'|\'). When collecting cores via a pipe to an application, it is occasionally useful for the collecting application to gather data about the crashing process from its /proc/pid directory. In order to do this safely, the kernel must wait for the collecting process to exit, so as not to remove the crashing processes proc files prematurely. This in turn creates the possibility that a misbehaving userspace collecting process can block the reaping of a crashed process simply by never exiting. This sysctl defends against that. It defines how many concurrent crashing processes may be piped to user space applications in parallel. If this value is exceeded, then those crashing processes above that value are noted via the kernel log and their cores are skipped. \n0 is a special value, indicating that unlimited processes may be captured in parallel, but that no waiting will take place (i.e. the collecting process is not guaranteed access to /proc//).\nDefault: 0'), 'kernel.core_uses_pid':N_('The default coredump filename is "core". By setting core_uses_pid to 1, the coredump filename becomes core.PID. If core_pattern does not include "%p" (default does not) and core_uses_pid is set, then .PID will be appended to the filename.'), 'kernel.ctrl-alt-del':N_('When the value in this file is 0, ctrl-alt-del is trapped and sent to the init(1) program to handle a graceful restart. When, however, the value is > 0, Linux\'s reaction to a Vulcan Nerve Pinch (tm) will be an immediate reboot, without even syncing its dirty buffers.'), 'kernel.dmesg_restrict':N_('This toggle indicates whether unprivileged users are prevented from using dmesg(8) to view messages from the kernel\'s log buffer. When dmesg_restrict is set to (0) there are no restrictions. When dmesg_restrict is set set to (1), users must have CAP_SYSLOG to use dmesg(8)'), 'kernel.domainname':N_('These can be used to set the NIS/YP domainname of your box in exactly the same way as the commands domainname'), 'kernel.ftrace_dump_on_oops':N_('Make ftrace dump on kernel oops. \nSet to "1" for enable\nSet to "0" for disable'), 'kernel.ftrace_enabled':N_('Ftrace will dump the trace buffers on oops. \nSet to "1" for enable\nSet to "0" for disable'), 'kernel.hostname':N_('These can be used to set the NIS/YP hostname of your box in exactly the same way as the commands hostname'), 'kernel.hotplug':N_('Path for the hotplug policy agent. \nDefault value is "/sbin/hotplug".'), #'kernel.io_delay_type':N_(''), #'kernel.java-appletviewer':N_(''), #'kernel.java-interpreter':N_(''), #'kernel.keys':N_(''), #'kernel.keys.gc_delay':N_(''), #'kernel.keys.maxbytes':N_(''), #'kernel.keys.maxkeys':N_(''), #'kernel.keys.root_maxbytes':N_(''), #'kernel.keys.root_maxkeys':N_(''), 'kernel.kptr_restrict':N_('This toggle indicates whether restrictions are placed on exposing kernel addresses via /proc and other interfaces. When kptr_restrict is set to (0), there are no restrictions. When kptr_restrict is set to (1), the default, kernel pointers printed using the %pK format specifier will be replaced with 0\'sunless the user has CAP_SYSLOG. When kptr_restrict is set to(2), kernel pointers printed using %pK will be replaced with 0\'sregardless of privileges.'), 'kernel.kstack_depth_to_print':N_('Controls the number of words to print when dumping the raw kernel stack.'), #'kernel.latencytop':N_(''), 'kernel.l2cr':N_('This flag controls the L2 cache of G3 processor boards. If 0, the cache is disabled. Enabled if nonzero.'), #'kernel.max_lock_depth':N_(''), #'kernel.modprobe':N_(''), 'kernel.modules_disabled':N_('A toggle value indicating if modules are allowed to be loaded in an otherwise modular kernel. This toggle defaults to off (0), but can be set true (1). Once true, modules can be neither loaded nor unloaded, and the toggle cannot be set back to false.'), #'kernel.msgmax':N_(''), #'kernel.msgmnb':N_(''), #'kernel.msgmni':N_(''), #'kernel.ngroups_max':N_(''), 'kernel.nmi_watchdog':N_('Enables/Disables the NMI watchdog on x86 systems. When the value is non-zero the NMI watchdog is enabled and will continuously test all online cpus to determine whether or not they are still functioning properly. Currently, passing "nmi_watchdog=" parameter at boot time is required for this function to work.'), 'kernel.osrelease':N_('Value contain OS release version'), 'kernel.ostype':N_('Value contain os type'), 'kernel.overflowgid':N_('If your architecture did not always support 32-bit UIDs (i.e. arm, i386, m68k, sh, and sparc32), a fixed UID will be returned to applications that use the old 16-bit UID system calls, if the actual UID would exceed 65535. These variable allow you to change the value of the fixed UID.\nDefault: 65534'), 'kernel.overflowuid':N_('If your architecture did not always support 32-bit GIDs (i.e. arm, i386, m68k, sh, and sparc32), a fixed GID will be returned to applications that use the old 16-bit GID system calls, if the actual GID would exceed 65535. These variable allow you to change the value of the fixed GID.\nDefault: 65534'), 'kernel.panic':N_('The value represents the number of seconds the kernel waits before rebooting on a panic. When you use the software watchdog, the recommended setting is 60.'), #'kernel.panic_on_io_nmi':N_(''), 'kernel.panic_on_oops':N_('Controls the kernel\'s behaviour when an oops or BUG is encountered.\n\n0: try to continue operation\n1: panic immediately. If the panic sysctl is also non-zero then the machine will be rebooted.'), 'kernel.panic_on_stackoverflow':N_('Controls the kernel\'s behavior when detecting the overflows of kernel, IRQ and exception stacks except a user stack. This file shows up if CONFIG_DEBUG_STACKOVERFLOW is enabled.\n0: try to continue operation.\n1: panic immediately.'), 'kernel.panic_on_unrecovered_nmi':N_('The default Linux behaviour on an NMI of either memory or unknown is to continue operation. For many environments such as scientific computing it is preferable that the box is taken out and the error dealt with than an uncorrected parity/ECC error get propogated.'), #'kernel.perf_event_max_sample_rate':N_(''), #'kernel.perf_event_mlock_kb':N_(''), #'kernel.perf_event_paranoid':N_(''), 'kernel.pid_max':N_('PID allocation wrap value. When the kernel\'s next PID value reaches this value, it wraps back to a minimum PID value. PIDs of value pid_max or larger are not allocated.'), #'kernel.poweroff_cmd':N_(''), 'kernel.powersave-nap':N_('If set, Linux-PPC will use the \'nap\' mode of powersaving, otherwise the \'doze\' mode will be used.'), #'kernel.print-fatal-signals':N_(''), 'kernel.printk':N_('The four values in printk denote: console_loglevel, default_message_loglevel, minimum_console_loglevel and default_console_log level respectively.\n\nThese values influence printk() behavior when printing or logging error messages. See \'man 2 syslog\' for more info on the different loglevels.\n- console_loglevel: messages with a higher priority than this will be printed to the console\n- default_message_loglevel: messages without an explicit priority will be printed with this priority\n- minimum_console_loglevel: minimum (highest) value to which console_loglevel can be set\n- default_console_loglevel: default value for console_loglevel'), 'kernel.printk_delay':N_('Delay each printk message in printk_delay milliseconds. Value from 0 - 10000 is allowed.'), 'kernel.printk_ratelimit':N_('Some warning messages are rate limited. printk_ratelimit specifies the minimum length of time between these messages (in jiffies), by default we allow one every 5 seconds. A value of 0 will disable rate limiting.'), 'kernel.printk_ratelimit_burst':N_('While long term we enforce one message per printk_ratelimit seconds, we do allow a burst of messages to pass through. printk_ratelimit_burst specifies the number of messages we can send before ratelimiting kicks in.'), #'kernel.pty':N_(''), #'kernel.pty.max':N_(''), #'kernel.pty.nr':N_(''), #'kernel.pty.reserve':N_(''), #'kernel.random':N_(''), #'kernel.random.boot_id':N_(''), #'kernel.random.entropy_avail':N_(''), #'kernel.random.poolsize':N_(''), #'kernel.random.read_wakeup_threshold':N_(''), #'kernel.random.write_wakeup_threshold':N_(''), #'kernel.random.uuid':N_(''), #'kernel.randomize_va_space':N_(''), #'kernel.real-root-dev':N_(''), #'kernel.sem':N_(''), #'kernel.sg-big-buff':N_(''), 'kernel.shmall':N_('This parameter sets the total amount of shared memory pages that can be used system wide. Hence, SHMALL should always be at least ceil(shmmax/PAGE_SIZE). The default size for SHMALL is 2097152. That means 2097152*4096 bytes (shmall*PAGE_SIZE) which is 8 GB for max shared memmory size.'), 'kernel.shmmax':N_('This value can be used to query and set the run time limit on the maximum shared memory segment size that can be created. Shared memory segments up to 1Gb are now supported in the kernel.\nDefault: SHMMAX.'), 'kernel.shmmni':N_('This parameter sets the system wide maximum number of shared memory segments.\nDefault: 4096'), #'kernel.shm_rmid_forced':N_(''), 'kernel.sched_autogroup_enabled':N_('This value that changes substantially how the process scheduler assigns shares of CPU time to each process. With this feature the system will group all processes with the same session ID as a single scheduling entity. Set to "1" for enable OR "0" for disable process auto group.'), 'kernel.sched_cfs_bandwidth_slice_us':N_('For efficiency run-time is transferred between the global pool and CPU local "silos" in a batch fashion. This greatly reduces global accounting pressure on large systems. The amount transferred each time such an update is required is described as the "slice".\nLarger slice values will reduce transfer overheads, while smaller values allow for more fine-grained consumption.\nDefault=5ms\nUnit = us'), #'kernel.sched_domain.*.*.busy_factor':N_(''), #'kernel.sched_domain.*.*.busy_idx':N_(''), #'kernel.sched_domain.*.*.cache_nice_tries':N_(''), #'kernel.sched_domain.*.*.flags':N_(''), #'kernel.sched_domain.*.*.forkexec_idx':N_(''), #'kernel.sched_domain.*.*.idle_idx':N_(''), #'kernel.sched_domain.*.*.imbalance_pct':N_(''), #'kernel.sched_domain.*.*.max_interval':N_(''), #'kernel.sched_domain.*.*.min_interval':N_(''), #'kernel.sched_domain.*.*.name':N_(''), #'kernel.sched_domain.*.*.newidle_idx':N_(''), #'kernel.sched_domain.*.*.wake_idx':N_(''), 'kernel.sched_child_runs_first':N_('A freshly forked child runs before the parent continues execution. Setting this parameter to 1 is beneficial for an application in which the child performs an execution after fork. For example make -j performs better when sched_child_runs_first is turned off.\nDefault: 0'), 'kernel.sched_compat_yield':N_('Enables the aggressive yield behavior of the old 0(1) scheduler. Java applications that use synchronization extensively perform better with this value set to 1. Only use it when you see a drop in performance. \nExpect applications that depend on the sched_yield() syscall behavior to perform better with the value set to 1.\nDefault: 0'), 'kernel.sched_features':N_('Provides information about specific debugging features.'), 'kernel.sched_latency_ns':N_('Targeted preemption latency for CPU bound tasks. Increasing this variable increases a CPU bound task\'s timeslice. A task\'s timeslice is its weighted fair share of the scheduling period:\ntimeslice = scheduling period * (task\'s weight/total weight of tasks in the run queue) The task\'s weight depends on the task\'s nice level and the scheduling policy. Minimum task weight for a SCHED_OTHER task is 15, corresponding to nice 19. The maximum task weight is 88761, corresponding to nice -20.\nTimeslices become smaller as the load increases. When the number of runnable tasks exceeds sched_latency_ns/sched_min_granularity_ns, the slice becomes number_of_running_tasks * sched_min_granularity_ns. Prior to that, the slice is equal to sched_latency_ns.\nThis value also specifies the maximum amount of time during which a sleeping task is considered to be running for entitlement calculations. Increasing this variable increases the amount of time a waking task may consume before being preempted, thus increasing scheduler latency for CPU bound tasks.\nDefault: 20000000\nUnit: ns'), 'kernel.sched_migration_cost_ns':N_('Amount of time after the last execution that a task is considered to be "cache hot" in migration decisions. A "hot" task is less likely to be migrated, so increasing this variable reduces task migrations.\nIf the CPU idle time is higher than expected when there are runnable processes, try reducing this value. If tasks bounce between CPUs or nodes too often, try increasing it.\nDefault: 500000\nUnit: ns'), 'kernel.sched_min_granularity_ns':N_('The minimum time after which a task become eligible to be preempted. The minimum possible preemption granularity\nDefault: 4000000\nUnit: ns'), 'kernel.sched_nr_migrate':N_('Controls how many tasks can be moved across processors through migration software interrupts (softirq). If a large number of tasks is created by SCHED_OTHER policy, they will all be run on the same processor. The default value is 32. Increasing this value gives a performance boost to large SCHED_OTHER threads at the expense of increased latencies for real-time tasks.\nDefault: 32'), 'kernel.sched_rt_period_us':N_('Period over which real-time task bandwidth enforcement is measured.\nDefault: 1000000\nUnit: us'), 'kernel.sched_rt_runtime_us':N_('Quantum allocated to real-time tasks during sched_rt_period_us. Setting to -1 disables RT bandwidth enforcement. By default, RT tasks may consume 95%CPU/sec, thus leaving 5%CPU/sec or 0.05s to be used by SCHED_OTHER tasks. Unit = us'), #'kernel.sched_shares_window_ns':N_(''), 'kernel.sched_stat_granularity_ns':N_('Specifies the granularity for collecting task scheduler statistics.\nUnit: ns'), 'kernel.sched_time_avg_ms':N_('Period over which we average the RT time consumption.\nDefault: 4ms\nUnit = ms'), 'kernel.sched_tunable_scaling':N_('The initial and re-scaling of tunables\nDefault: Scaled logarithmically\nScaling now takes place on all kind of cpu add/remove events.'), 'kernel.sched_wakeup_granularity_ns':N_('The wake-up preemption granularity. Increasing this variable reduces wake-up preemption, reducing disturbance of compute bound tasks. Lowering it improves wake-up latency and throughput for latency critical tasks, particularly when a short duty cycle load component must compete with CPU bound components.\n\nSettings larger than half of sched_latency_ns will result in zero wake-up preemption and short duty cycle tasks will be unable to compete with CPU hogs effectively.\n\nDefault: 5000000\nUnit: ns'), #'kernel.softlockup_panic':N_(''), #'kernel.stack_tracer_enabled':N_(''), #'kernel.sysrq':N_(''), 'kernel.tainted':N_('Non-zero if the kernel has been tainted. Numeric values, which can be ORed together:\n\n - 1 - A module with a non-GPL license has been loaded, this includes modules with no license. Set by modutils >= 2.4.9 and module-init-tools.\n - 2 - A module was force loaded by insmod -f. Set by modutils >= 2.4.9 and module-init-tools.\n - 4 - Unsafe SMP processors: SMP with CPUs not designed for SMP.\n - 8 - A module was forcibly unloaded from the system by rmmod -f.\n - 16 - A hardware machine check error occurred on the system.\n - 32 - A bad page was discovered on the system.\n - 64 - The user has asked that the system be marked "tainted". This could be because they are running software that directly modifies the hardware, or for other reasons.\n - 128 - The system has died.\n - 256 - The ACPI DSDT has been overridden with one supplied by the user instead of using the one provided by the hardware.\n - 512 - A kernel warning has occurred.\n - 1024 - A module from drivers/staging was loaded.'), #'kernel.threads-max':N_(''), #'kernel.timer_migration':N_(''), 'kernel.unknown_nmi_panic':N_('The value in this file affects behavior of handling NMI. When the value is non-zero, unknown NMI is trapped and then panic occurs. At that time, kernel debugging information is displayed on console.'), #'kernel.usermodehelper':N_(''), #'kernel.usermodehelper.bset':N_(''), #'kernel.usermodehelper.inheritable':N_(''), #'kernel.version':N_(''), #'kernel.watchdog':N_(''), #'kernel.watchdog_thresh':N_(''), 'vm.block_dump':N_('Block_dump enables block I/O debugging when set to a nonzero value'), 'vm.compact_memory':N_('When 1 is written to the file, all zones are compacted such that free memory is available in contiguous blocks where possible. This can be important for example in the allocation of huge pages although processes will also directly compact memory as required.'), 'vm.dirty_background_bytes':N_('Contains the amount of dirty memory at which the pdflush background writeback daemon will start writeback.'), 'vm.dirty_background_ratio':N_('Contains, as a percentage of total system memory, the number of pages at which the pdflush background writeback daemon will start writing out dirty data.'), 'vm.dirty_bytes':N_('Contains the amount of dirty memory at which a process generating disk writes will itself start writeback.'), 'vm.dirty_expire_centisecs':N_('This tunable is used to define when dirty data is old enough to be eligible for writeout by the pdflush daemons. It is expressed in 100\'ths of a second. Data which has been dirty in-memory for longer than this interval will be written out next time a pdflush daemon wakes up.'), 'vm.dirty_ratio':N_('Contains, as a percentage of total system memory, the number of pages at which a process which is generating disk writes will itself start writing out dirty data.'), 'vm.dirty_writeback_centisecs':N_('The pdflush writeback daemons will periodically wake up and write old data out to disk. This tunable expresses the interval between those wakeups, in 100\'ths of a second. Setting this to zero disables periodic writeback altogether.'), 'vm.drop_caches':N_('Writing to this will cause the kernel to drop clean caches, dentries and inodes from memory, causing that memory to become free. To free pagecache set to 1, To free dentries and inodes set to 2 and to free pagecache, dentries and inodes set to 3. As this is a non-destructive operation and dirty objects are not freeable, the user should run sync first.'), 'vm.extfrag_threshold':N_('This parameter affects whether the kernel will compact memory or direct reclaim to satisfy a high-order allocation. /proc/extfrag_index shows what the fragmentation index for each order is in each zone in the system. Values tending towards 0 imply allocations would fail due to lack of memory, values towards 1000 imply failures are due to fragmentation and -1 implies that the allocation will succeed as long as watermarks are met. The kernel will not compact memory in a zone if the fragmentation index is <= extfrag_threshold.\nDefault: 500'), 'vm.hugepages_treat_as_movable':N_('This parameter is only useful when kernelcore= is specified at boot time to create ZONE_MOVABLE for pages that may be reclaimed or migrated. Huge pages are not movable so are not normally allocated from ZONE_MOVABLE. A non-zero value written to hugepages_treat_as_movable allows huge pages to be allocated from ZONE_MOVABLE. Once enabled, the ZONE_MOVABLE is treated as an area of memory the huge pages pool can easily grow or shrink within. Assuming that applications are not running that mlock() a lot of memory, it is likely the huge pages pool can grow to the size of ZONE_MOVABLE by repeatedly entering the desired value into nr_hugepages and triggering page reclaim.'), 'vm.hugetlb_shm_group':N_('hugetlb_shm_group contains group id that is allowed to create SysV shared memory segment using hugetlb page.'), 'vm.laptop_mode':N_('laptop_mode is a knob that controls "laptop mode".'), 'vm.legacy_va_layout':N_('If non-zero, this sysctl disables the new 32-bit mmap layout - the kernel will use the legacy (2.4) layout for all processes.'), 'vm.lowmem_reserve_ratio':N_('For some specialised workloads on highmem machines it is dangerous for the kernel to allow process memory to be allocated from the "lowmem" zone. This is because that memory could then be pinned via the mlock() system call, or by unavailability of swapspace. Please do not change this value unless you really know what you are doing'), 'vm.max_map_count':N_('This file contains the maximum number of memory map areas a process may have. Memory map areas are used as a side-effect of calling malloc, directly by mmap and mprotect, and also when loading shared libraries. While most applications need less than a thousand maps, certain programs, particularly malloc debuggers, may consume lots of them, e.g., up to one or two maps per allocation.\nDefault: 65536'), 'vm.memory_failure_early_kill':N_('Control how to kill processes when uncorrected memory error (typically a 2bit error in a memory module) is detected in the background by hardware that cannot be handled by the kernel. In some cases (like the page still having a valid copy on disk) the kernel will handle the failure transparently without affecting any applications. But if there is no other uptodate copy of the data it will kill to prevent any data corruptions from propagating. \n\n1: Kill all processes that have the corrupted and not reloadable page mapped as soon as the corruption is detected. Note this is not supported for a few types of pages, like kernel internally allocated data or the swap cache, but works for the majority of user pages. \n\n0: Only unmap the corrupted page from all processes and only kill a process who tries to access it.'), 'vm.memory_failure_recovery':N_('Enable memory failure recovery (when supported by the platform) \n\n1: Attempt recovery.\n\n0: Always panic on a memory failure.'), 'vm.min_free_kbytes':N_('This is used to force the Linux VM to keep a minimum number of kilobytes free. The VM uses this number to compute a watermark[WMARK_MIN] value for each lowmem zone in the system. Each lowmem zone gets a number of reserved free pages based proportionally on its size. \n\nSome minimal amount of memory is needed to satisfy PF_MEMALLOC allocations; if you set this to lower than 1024KB, your system will become subtly broken, and prone to deadlock under high loads.\n\nSetting this too high will OOM your machine instantly.'), 'vm.min_slab_ratio':N_('A percentage of the total pages in each zone. On Zone reclaim (fallback from the local zone occurs) slabs will be reclaimed if more than this percentage of pages in a zone are reclaimable slab pages. This insures that the slab growth stays under control even in NUMA systems that rarely perform global reclaim. \nDefault: 5\nUnit: percent'), 'vm.min_unmapped_ratio':N_('This is a percentage of the total pages in each zone. Zone reclaim will only occur if more than this percentage of pages are in a state that zone_reclaim_mode allows to be reclaimed. \n\n If zone_reclaim_mode has the value 4 OR\'d, then the percentage is compared against all file-backed unmapped pages including swapcache pages and tmpfs files. Otherwise, only unmapped pages backed by normal files but not tmpfs files and similar are considered.\nDefault: 1\nUnit: percent'), 'vm.mmap_min_addr':N_('This file indicates the amount of address space which a user process will be restricted from mmapping. Since kernel null dereference bugs could accidentally operate based on the information in the first couple of pages of memory userspace processes should not be allowed to write to them. By default this value is set to 0 and no protections will be enforced by the security module. Setting this value to something like 64k will allow the vast majority of applications to work correctly and provide defense in depth against future potential kernel bugs.'), 'vm.nr_hugepages':N_('Change the minimum size of the hugepage pool.'), #'vm.nr_hugepages_mempolicy':N_(''), 'vm.nr_overcommit_hugepages':N_('Change the maximum size of the hugepage pool. The maximum is nr_hugepages + nr_overcommit_hugepages.'), 'vm.nr_pdflush_threads':N_('The current number of pdflush threads. This value is read-only. The value changes according to the number of dirty pages in the system.\n\nWhen necessary, additional pdflush threads are created, one per second, up to nr_pdflush_threads_max.'), 'vm.nr_trim_pages':N_('This value adjusts the excess page trimming behaviour of power-of-2 aligned NOMMU mmap allocations. \n\nA value of 0 disables trimming of allocations entirely, while a value of 1 trims excess pages aggressively. Any value >= 1 acts as the watermark where trimming of allocations is initiated.\nDefault: 1'), #'vm.numa_zonelist_order':N_(''), 'vm.oom_dump_tasks':N_('Enables a system-wide task dump (excluding kernel threads) to be produced when the kernel performs an OOM-killing and includes such information as pid, uid, tgid, vm size, rss, cpu, oom_adj score, and name. This is helpful to determine why the OOM killer was invoked and to identify the rogue task that caused it.\n\nIf this is set to zero, this information is suppressed. On very large systems with thousands of tasks it may not be feasible to dump the memory state information for each one. Such systems should not be forced to incur a performance penalty in OOM conditions when the information may not be desired.\n\nIf this is set to non-zero, this information is shown whenever the OOM killer actually kills a memory-hogging task.\nDefault: 1 (enabled)'), 'vm.oom_kill_allocating_task':N_('This enables or disables killing the OOM-triggering task in out-of-memory situations.\n\nIf this is set to zero, the OOM killer will scan through the entire tasklist and select a task based on heuristics to kill. This normally selects a rogue memory-hogging task that frees up a large amount of memory when killed.\n\nIf this is set to non-zero, the OOM killer simply kills the task that triggered the out-of-memory condition. This avoids the expensive tasklist scan.\n\nIf panic_on_oom is selected, it takes precedence over whatever value is used in oom_kill_allocating_task.\nDefault 0'), 'vm.overcommit_memory':N_('This value contains a flag that enables memory overcommitment.\n\nWhen this flag is 0, the kernel attempts to estimate the amount of free memory left when userspace requests more memory. When this flag is 1, the kernel pretends there is always enough memory until it actually runs out.\n\nWhen this flag is 2, the kernel uses a "never overcommit" policy that attempts to prevent any overcommit of memory.\n\nThis feature can be very useful because there are a lot of programs that malloc() huge amounts of memory "just-in-case" and don\'t use much of it.\nDefault: 0'), 'vm.overcommit_ratio':N_('When overcommit_memory is set to 2, the committed address space is not permitted to exceed swap plus this percentage of physical RAM.'), 'vm.page-cluster':N_('page-cluster controls the number of pages which are written to swap in a single attempt. The swap I/O size. It is a logarithmic value - setting it to zero means "1 page", setting it to 1 means "2 pages", setting it to 2 means "4 pages", etc. The default value is three (eight pages at a time). There may be some small benefits in tuning this to a different value if your workload is swap-intensive.'), 'vm.panic_on_oom':N_('This enables or disables panic on out-of-memory feature.\n\nIf this is set to 0, the kernel will kill some rogue process, called oom_killer. Usually, oom_killer can kill rogue processes and system will survive.\n\nIf this is set to 1, the kernel panics when out-of-memory happens. However, if a process limits using nodes by mempolicy/cpusets, and those nodes become memory exhaustion status, one process may be killed by oom-killer. No panic occurs in this case.Because other nodes memory may be free. This means system total status may be not fatal yet.\n\nIf this is set to 2, the kernel panics compulsorily even on the above-mentioned. Even oom happens under memory cgroup, the whole system panics. The default value is 0. 1 and 2 are for failover of clustering. Please select either according to your policy of failover. panic_on_oom=2+kdump gives you very strong tool to investigate why oom happens. You can get snapshot.'), 'vm.percpu_pagelist_fraction':N_('This is the fraction of pages at most (high mark pcp->high) in each zone that are allocated for each per cpu page list. The min value for this is 8. It means that we don\'t allow more than 1/8th of pages in each zone to be allocated in any single per_cpu_pagelist. This entry only changes the value of hot per cpu pagelists. User can specify a number like 100 to allocate 1/100th of each zone to each per cpu page list.\n\nThe batch value of each per cpu pagelist is also updated as a result. It is set to pcp->high/4. The upper limit of batch is (PAGE_SHIFT * 8)\n\nThe initial value is zero. Kernel does not use this value at boot time to set the high water marks for each per cpu page list.'), #'vm.scan_unevictable_pages':N_(''), 'vm.stat_interval':N_('The time interval between which vm statistics are updated.\nDefault: 1\nUnit: seconds'), 'vm.swappiness':N_('This control is used to define how aggressive the kernel will swap memory pages. Higher values will increase agressiveness, lower values decrease the amount of swap.\nDefault: 60\nUnit: percent'), 'vm.vfs_cache_pressure':N_('Controls the tendency of the kernel to reclaim the memory which is used for caching of directory and inode objects.\n\nAt the default value of vfs_cache_pressure=100 the kernel will attempt to reclaim dentries and inodes at a "fair" rate with respect to pagecache and swapcache reclaim. Decreasing vfs_cache_pressure causes the kernel to prefer to retain dentry and inode caches. When vfs_cache_pressure=0, the kernel will never reclaim dentries and inodes due to memory pressure and this can easily lead to out-of-memory conditions. Increasing vfs_cache_pressure beyond 100 causes the kernel to prefer to reclaim dentries and inodes.'), 'vm.zone_reclaim_mode':N_('Zone_reclaim_mode allows someone to set more or less aggressive approaches to reclaim memory when a zone runs out of memory. If it is set to zero then no zone reclaim occurs. Allocations will be satisfied from other zones / nodes in the system.\nThis is value ORed together of\n\n1 = Zone reclaim on\n2 = Zone reclaim writes dirty pages out\n4 = Zone reclaim swaps pages') } tuna-0.19/tuna/new_eth.py000077500000000000000000000016661437350234000154010ustar00rootroot00000000000000# Copyright (C) 2022 John Kacur """ A few functions similar to ethtool """ import os import socket def get_active_devices(): """ return a list of network devices """ ret = [] for device in socket.if_nameindex(): ret.append(device[1]) return ret def get_module(intf): """ return the kernel module for the given network interface """ if intf == 'lo': return "" myp = f'/sys/class/net/{intf}/device/driver' if os.path.exists(myp): return os.path.basename(os.readlink(myp)) if os.path.exists(f'/sys/class/net/{intf}/bridge'): return 'bridge' if os.path.exists(f'/sys/class/net/{intf}/tun_flags'): return 'tun' return "" if __name__ == "__main__": nics = get_active_devices() print(f'nics = {nics}') for intf in nics: driver = get_module(intf) if driver: print(f'{intf}, {driver}') else: print(f'{intf}') tuna-0.19/tuna/oscilloscope.py000077500000000000000000000375411437350234000164470ustar00rootroot00000000000000# Oscilloscope # # Copyright 2008-2009 Red Hat, Inc. # # Arnaldo Carvalho de Melo # # Please check the tuna repository at: # http://git.kernel.org/?p=linux/kernel/git/acme/tuna.git;a=tree # For newer versions and to see it integrated with tuna # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA import os import sys import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject from matplotlib.backends.backend_gtk3agg import \ FigureCanvasGTK3Agg as figure_canvas #from matplotlib.backends.backend_agg import \ # FigureCanvasAgg as figure_canvas import matplotlib.figure import matplotlib.ticker import numpy class histogram_frame(Gtk.Frame): def __init__(self, title="Statistics", width=780, height=100, max_value=500, nr_entries=10, facecolor="white"): Gtk.Frame.__init__(self) self.set_label(title) self.fraction = int(max_value / nr_entries) if self.fraction == 0: self.fraction = max_value nr_entries = 1 self.max_value = max_value self.nr_entries = nr_entries self.nr_samples = 0 table = Gtk.Table(3, self.nr_entries + 1, False) table.set_border_width(5) table.set_row_spacings(5) table.set_col_spacings(10) self.add(table) self.buckets = [0, ] * (nr_entries + 1) self.buckets_bar = [None, ] * (nr_entries + 1) self.buckets_counter = [None, ] * (nr_entries + 1) prefix = "<=" for bucket in range(self.nr_entries + 1): bucket_range = (bucket + 1) * self.fraction if bucket_range > self.max_value: prefix = ">" bucket_range = self.max_value label = Gtk.Label("%s %d" % (prefix, bucket_range)) label.set_alignment(0, 1) table.attach(label, 0, 1, bucket, bucket + 1, 0, 0, 0, 0) self.buckets_bar[bucket] = Gtk.ProgressBar() table.attach(self.buckets_bar[bucket], 1, 2, bucket, bucket + 1, 0, 0, 0, 0) self.buckets_counter[bucket] = Gtk.Label(label="0") label.set_alignment(0, 1) table.attach(self.buckets_counter[bucket], 2, 3, bucket, bucket + 1, 0, 0, 0, 0) self.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(facecolor)) def add_sample(self, sample): if sample > self.max_value: bucket = self.nr_entries else: bucket = int(sample / self.fraction) self.nr_samples += 1 self.buckets[bucket] += 1 def refresh(self): for bucket in range(self.nr_entries + 1): self.buckets_counter[bucket].set_text(str(self.buckets[bucket])) fraction = float(self.buckets[bucket]) / self.nr_samples self.buckets_bar[bucket].set_fraction(fraction) def reset(self): self.buckets = [0, ] * (self.nr_entries + 1) self.nr_samples = 0 class oscilloscope_frame(Gtk.Frame): def __init__(self, title="Osciloscope", width=780, height=360, nr_samples_on_screen=250, graph_type='-', max_value=500, plot_color="lightgreen", bg_color="darkgreen", facecolor="white", ylabel="Latency", picker=None): Gtk.Frame.__init__(self) self.set_label(title) self.font = {'fontname' : 'Liberation Sans', 'color' : 'b', 'fontweight' : 'bold', 'fontsize' : 10} self.max_value = max_value self.nr_samples_on_screen = nr_samples_on_screen self.ind = numpy.arange(nr_samples_on_screen) self.samples = [0.0] * nr_samples_on_screen figure = matplotlib.figure.Figure(figsize=(10, 4), dpi=100, facecolor=facecolor) ax = figure.add_subplot(111) self.ax = ax ax.set_facecolor(bg_color) self.on_screen_samples = ax.plot(self.ind, self.samples, graph_type, color=plot_color, picker=picker) ax.set_ylim(0, max_value) ax.set_ylabel(ylabel, self.font) ax.set_xlabel("%d samples" % nr_samples_on_screen, self.font) ax.set_xticklabels([]) ax.grid(True) for label in ax.get_yticklabels(): label.set(fontsize=8) self.canvas = figure_canvas(figure) # a Gtk.DrawingArea self.canvas.set_size_request(width, height) self.add(self.canvas) self.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(facecolor)) self.nr_samples = 0 def add_sample(self, sample): del self.samples[0] self.samples.append(sample) self.on_screen_samples[0].set_data(self.ind, self.samples) self.nr_samples += 1 if self.nr_samples <= self.nr_samples_on_screen: self.ax.set_xlabel("%d samples" % self.nr_samples, self.font) def reset(self): self.samples = [0.0] * self.nr_samples_on_screen self.nr_samples = 0 self.on_screen_samples[0].set_data(self.ind, self.samples) def refresh(self): self.canvas.draw() return def add_table_row(table, row, label_text, label_value="0"): label = Gtk.Label(label=label_text) label.set_use_underline(True) label.set_alignment(0, 1) table.attach(label, 0, 1, row, row + 1, 0, 0, 0, 0) label = Gtk.Label(label=label_value) table.attach(label, 1, 2, row, row + 1, 0, 0, 0, 0) return label class system_info_frame(Gtk.Frame): def __init__(self, title="System", facecolor="white"): Gtk.Frame.__init__(self) self.set_label(title) self.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(facecolor)) table = Gtk.Table(3, 2, False) table.set_border_width(5) table.set_row_spacings(5) table.set_col_spacings(10) self.add(table) u = os.uname() add_table_row(table, 0, "Kernel Release", u[2]) add_table_row(table, 1, "Architecture", u[4]) add_table_row(table, 2, "Machine", u[1]) class oscilloscope(Gtk.Window): def __init__(self, get_sample=None, width=800, height=500, nr_samples_on_screen=250, graph_type='-', title="Osciloscope", max_value=500, plot_color="lightgreen", bg_color="darkgreen", facecolor="white", ylabel="Latency", picker=None, snapshot_samples=0, geometry=None, scale=True): Gtk.Window.__init__(self) if geometry: self.parse_geometry(geometry) width, height = self.get_size() else: self.set_default_size(width, height) self.get_sample = get_sample self.max_value = max_value self.snapshot_samples = snapshot_samples self.scale = scale self.set_title(title) vbox = Gtk.VBox() vbox.set_border_width(8) self.add(vbox) stats_frame = Gtk.Frame() stats_frame.set_label("Statistics") stats_frame.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(facecolor)) table = Gtk.Table(3, 2, False) table.set_border_width(5) table.set_row_spacings(5) table.set_col_spacings(10) stats_frame.add(table) self.min_label = add_table_row(table, 0, "Min") self.avg_label = add_table_row(table, 1, "Avg") self.max_label = add_table_row(table, 2, "Max") help_frame = Gtk.Frame() help_frame.set_label("Help") help_frame.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse(facecolor)) table = Gtk.Table(4, 2, False) table.set_border_width(5) table.set_row_spacings(5) table.set_col_spacings(10) help_frame.add(table) add_table_row(table, 0, "Space", "Pause") add_table_row(table, 1, "S", "Snapshot") add_table_row(table, 2, "R", "Reset") add_table_row(table, 3, "Q", "Quit") self.scope = oscilloscope_frame("Scope", int(width * 0.94), int(height * 0.64), nr_samples_on_screen, max_value=max_value, graph_type=graph_type, picker=picker, ylabel=ylabel) self.hist = histogram_frame("Histogram", 0, 0, nr_entries=5, max_value=max_value) info_frame = system_info_frame() vbox_help_info = Gtk.VBox() vbox_help_info.pack_start(info_frame, False, False, 0) vbox_help_info.pack_end(help_frame, False, False, 0) hbox = Gtk.HBox() hbox.pack_start(vbox_help_info, False, False, 0) hbox.pack_start(stats_frame, False, False, 0) hbox.pack_end(self.hist, True, True, 0) vbox.pack_start(self.scope, True, True, 0) vbox.pack_end(hbox, True, False, 0) self.show_all() self.getting_samples = False self.refreshing_screen = False self.max = self.min = None self.avg = 0 def add_sample(self, sample): if not self.max or self.max < sample: self.max = sample if not self.min or self.min > sample: self.min = sample self.avg = (self.avg + sample) / 2 self.scope.add_sample(sample) self.hist.add_sample(sample) def refresh(self): if self.scale and self.max > self.scope.max_value: self.scope.max_value *= 2 self.scope.ax.set_ylim(0, self.scope.max_value) self.scope.refresh() self.hist.refresh() while Gtk.events_pending(): Gtk.main_iteration() def get_samples(self, fd, condition): try: sample = self.get_sample() prev_min, prev_avg, prev_max = self.min, self.avg, self.max self.add_sample(sample) if self.refreshing_screen: if self.min != prev_min: self.min_label.set_text("%-6.3f" % self.min) if self.avg != prev_avg: self.avg_label.set_text("%-6.3f" % self.avg) if self.max != prev_max: self.max_label.set_text("%-6.3f" % self.max) self.refresh() if self.snapshot_samples == self.scope.nr_samples: self.snapshot() Gtk.main_quit() except: print("invalid sample, check the input format") pass return self.getting_samples def run(self, fd): self.connect("key_press_event", self.key_press_event) self.getting_samples = True self.refreshing_screen = True GObject.io_add_watch(fd, GObject.IO_IN | GObject.IO_PRI, self.get_samples) def freeze_screen(self, state=False): self.refreshing_screen = state def stop(self): self.getting_samples = False self.refreshing_screen = False def snapshot(self): self.scope.canvas.print_figure("scope_snapshot.svg") def reset(self): self.scope.max_value = self.max_value self.scope.ax.set_ylim(0, self.scope.max_value) self.scope.reset() self.hist.reset() self.min = self.max_value self.max = 0 self.avg = 0 def key_press_event(self, widget, event): if event.keyval == ord(' '): self.freeze_screen(not self.refreshing_screen) elif event.keyval in (ord('s'), ord('S')): self.snapshot() elif event.keyval in (ord('r'), ord('R')): self.reset() elif event.keyval in (ord('q'), ord('Q')): Gtk.main_quit() class ftrace_window(Gtk.Window): (COL_FUNCTION, ) = list(range(1)) def __init__(self, trace, parent=None): Gtk.Window.__init__(self) try: self.set_screen(parent.get_screen()) except AttributeError: self.connect('destroy', lambda *w: Gtk.main_quit()) self.set_border_width(8) self.set_default_size(350, 500) self.set_title("ftrace") vbox = Gtk.VBox(False, 8) self.add(vbox) sw = Gtk.ScrolledWindow() sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) vbox.pack_start(sw, True, True) store = Gtk.ListStore(GObject.TYPE_STRING) for entry in trace: if entry[0] in ["#", "\n"] or entry[:4] == "vim:": continue iter = store.append() store.set(iter, self.COL_FUNCTION, entry.strip()) treeview = Gtk.TreeView(store) treeview.set_rules_hint(True) column = Gtk.TreeViewColumn("Function", Gtk.CellRendererText(), text=self.COL_FUNCTION) treeview.append_column(column) sw.add(treeview) self.show_all() class cyclictestoscope(oscilloscope): def __init__(self, max_value, snapshot_samples=0, nr_samples_on_screen=500, delimiter=':', field=2, ylabel="Latency", geometry=None, scale=True, sample_multiplier=1): oscilloscope.__init__(self, self.get_sample, title="CyclictestoSCOPE", nr_samples_on_screen=nr_samples_on_screen, width=900, max_value=max_value, picker=self.scope_picker, snapshot_samples=snapshot_samples, ylabel=ylabel, geometry=geometry, scale=scale) self.connect("destroy", self.quit) self.delimiter = delimiter self.sample_multiplier = sample_multiplier self.field = field self.latency_tracer = os.access("/sys/kernel/debug/tracing/trace", os.R_OK) if self.latency_tracer: self.traces = [None,] * nr_samples_on_screen def scope_picker(self, line, mouseevent): if (not self.latency_tracer) or mouseevent.xdata is None: return False, dict() x = int(mouseevent.xdata) if self.traces[x]: fw = ftrace_window(self.traces[x], self) return False, dict() def get_sample(self): fields = sys.stdin.readline().split(self.delimiter) try: sample = float(fields[self.field]) * self.sample_multiplier except: print("fields=%s, self.field=%s,self.delimiter=%s" % (fields, self.field, self.delimiter)) return None if self.latency_tracer: del self.traces[0] if sample > self.avg: print(sample) try: f = open("/sys/kernel/debug/tracing/trace") trace = f.readlines() f.close() f = open("/sys/kernel/debug/tracing/tracing_max_latency", "w") f.write("0\n") f.close() except: trace = None else: print("-") trace = None self.traces.append(trace) return sample def run(self): oscilloscope.run(self, sys.stdin.fileno()) def quit(self, x): Gtk.main_quit() tuna-0.19/tuna/sysfs.py000077500000000000000000000100441437350234000151050ustar00rootroot00000000000000# -*- python -*- # -*- coding: utf-8 -*- """ classes for /sys/devices/system/cpu/ so we can get topology information and do CPU hotplug operations """ import os class cpu: """ class to query if a cpu is online or not and to bring a cpu online or to take it offline """ def __init__(self, basedir, name): self.name = name self.dir = os.path.join(basedir, name) self.online = None self.reload() def __lt__(self, other): # Previously it is assumed that name[:3] == "cpu" and name[3].isdigit(), so: return int(self.name[3:]) < int(other.name[3:]) def readfile(self, name): """ read file "name", typically the per cpu "online" file """ try: with open(os.path.join(self.dir, name)) as f: value = f.readline().strip() return value except: raise def __repr__(self): return f'name={self.name}, online={self.online}, physical_package_id={self.physical_package_id}' def reload_online(self): """ read the "online" file """ self.online = True try: self.online = self.readfile("online") == "1" except FileNotFoundError: # boot CPU, usually cpu0, can't be brought offline, so # lacks the file and non root users can't read. In both # cases assume CPU is online. pass def reload(self): """ Load or reload the values of this class """ self.reload_online() if self.online: try: self.physical_package_id = self.readfile("topology/physical_package_id") except: self.physical_package_id = "0" else: self.physical_package_id = None def set_online(self, online=True): """ Turn a cpu on or off using the online file """ try: with open(os.path.join(self.dir, "online"), "w") as f: f.write("1" if online else "0") except: pass self.reload_online() return online == self.online class cpus: """ a class that creates a dictionary of cpu information keyed by cpu """ def __init__(self, basedir="/sys/devices/system/cpu"): self.basedir = basedir self.cpus = {} self.sockets = {} self.reload() self.nr_cpus = len(self.cpus) def __getitem__(self, key): return self.cpus[key] def keys(self): """ Returns a list of keys, for example a key could be cpu9 """ return list(self.cpus.keys()) def __contains__(self, key): return key in self.cpus def __repr__(self): return f'cpus={self.cpus}, sockets={self.sockets}, nr_cpus={self.nr_cpus}' def reload(self): """ load or reload the values of this class """ sockets_to_sort = [] for name in os.listdir(self.basedir): if name[:3] != "cpu" or not name[3].isdigit(): continue if name in self.cpus: self.cpus[name].reload(self.basedir) else: c = cpu(self.basedir, name) self.cpus[name] = c try: socket = c.physical_package_id except: socket = "0" if socket in self.sockets: self.sockets[socket].insert(0, c) else: self.sockets[socket] = [c, ] if socket not in sockets_to_sort: sockets_to_sort.append(socket) for socket in sockets_to_sort: self.sockets[socket].sort(key=lambda x: int(x.name[3:])) if __name__ == '__main__': cpus = cpus() print(f'{cpus}') for socket in list(cpus.sockets.keys()): print("Socket %s" % socket) for c in cpus.sockets[socket]: print(" %s" % c.name) print(" online: %s" % c.online) c.set_online(False) print(" online: %s" % c.online) c.set_online() print(" online: %s" % c.online) tuna-0.19/tuna/tuna.py000077500000000000000000000551061437350234000147150ustar00rootroot00000000000000# -*- python -*- # -*- coding: utf-8 -*- import copy import errno import os import re import sys import shlex import fnmatch import platform import tuna.new_eth as ethtool import procfs from procfs import utilist from tuna import help from tuna import tuna_sched try: fntable except NameError: fntable = [] def kthread_help(key): if '/' in key: key = key[:key.rfind('/')+1] return help.KTHREAD_HELP.get(key, " ") def proc_sys_help(key): if not fntable: reg_match = ['[', '*', '?'] for value in help.PROC_SYS_HELP: for char in reg_match: if char in value: fntable.append(value) temp = help.PROC_SYS_HELP.get(key, "") if temp: return key + ":\n" + temp for value in fntable: if fnmatch.fnmatch(key, value): return key + ":\n" + help.PROC_SYS_HELP.get(value, "") return key def kthread_help_plain_text(pid, cmdline): cmdline = cmdline.split(' ')[0] params = {'pid':pid, 'cmdline':cmdline} if iskthread(pid): title = _("Kernel Thread %(pid)d (%(cmdline)s):") % params help = kthread_help(cmdline) else: title = _("User Thread %(pid)d (%(cmdline)s):") % params help = title return help, title def iskthread(pid): # FIXME: we should leave to the callers to handle all the exceptions, # in this function, so that they know that the thread vanished and # can act accordingly, removing entries from tree views, etc try: f = open(f"/proc/{pid}/smaps") except IOError: # Thread has vanished return True line = f.readline() f.close() if line: return False # Zombies also don't have smaps entries, so check the state: try: p = procfs.pidstat(pid) except: return True if p["state"] == 'Z': return False return True def irq_thread_number(cmd): if cmd[:4] == "irq/": return cmd[4:cmd.find('-')] if cmd[:4] == "IRQ-": return cmd[4:] raise LookupError def is_irq_thread(cmd): return cmd[:4] in ("IRQ-", "irq/") def threaded_irq_re(irq): return re.compile(f"(irq/{irq}-.+|IRQ-{irq})") # FIXME: Move to python-linux-procfs def has_threaded_irqs(ps): irq_re = re.compile("(irq/[0-9]+-.+|IRQ-[0-9]+)") return len(ps.find_by_regex(irq_re)) > 0 def set_irq_affinity_filename(filename, bitmasklist): pathname = f"/proc/irq/{filename}" f = open(pathname, "w") text = ",".join(["%x" % a for a in bitmasklist]) f.write(f"{text}\n") try: f.close() except IOError: # This happens with IRQ 0, for instance return False return True def set_irq_affinity(irq, bitmasklist): return set_irq_affinity_filename("%d/smp_affinity" % irq, bitmasklist) def cpustring_to_list(cpustr): """Convert a string of numbers to an integer list. Given a string of comma-separated numbers and number ranges, return a simple sorted list of the integers it represents. This function will throw exceptions for badly-formatted strings. Returns a list of integers.""" fields = cpustr.strip().split(",") cpu_list = [] for field in fields: ends = [int(a, 0) for a in field.split("-")] if len(ends) > 2: raise SyntaxError("Syntax error") if len(ends) == 2: cpu_list += list(range(ends[0], ends[1] + 1)) else: cpu_list += [ends[0]] return list(set(cpu_list)) def list_to_cpustring(l): """Convert a list of integers into a range string. Consecutive values will be collapsed into ranges. This should not throw any exceptions as long as the list is all positive integers. Returns a string.""" l = list(set(l)) strings = [] prev = -2 while l: i = l.pop(0) if i - 1 == prev: while l: j = l.pop(0) if j - 1 != i: l.insert(0, j) break i = j t = strings.pop() if int(t) + 1 == i: strings.append("%s,%u" % (t, i)) else: strings.append("%s-%u" % (t, i)) else: strings.append("%u" % i) prev = i return ",".join(strings) # FIXME: move to python-linux-procfs def is_hardirq_handler(self, pid): PF_HARDIRQ = 0x08000000 try: return int(self.processes[pid]["stat"]["flags"]) & \ PF_HARDIRQ and True or False except: return False def move_threads_to_cpu(cpus, pid_list, set_affinity_warning=None, spread=False): changed = False ps = procfs.pidstats() cpu_idx = 0 nr_cpus = len(cpus) new_affinity = cpus last_cpu = max(cpus) + 1 for pid in pid_list: if spread: new_affinity = [cpus[cpu_idx]] cpu_idx += 1 if cpu_idx == nr_cpus: cpu_idx = 0 try: try: curr_affinity = os.sched_getaffinity(pid) except OSError as err: if err.args[0] == errno.ESRCH: continue curr_affinity = None raise err if set(curr_affinity) != set(new_affinity): try: os.sched_setaffinity(pid, new_affinity) curr_affinity = os.sched_getaffinity(pid) except OSError as err: if err.args[0] == errno.ESRCH: continue curr_affinity = None raise err if set(curr_affinity) == set(new_affinity): changed = True if is_hardirq_handler(ps, pid): try: irq = int(ps[pid]["stat"]["comm"][4:]) bitmasklist = procfs.hexbitmask(new_affinity, last_cpu) set_irq_affinity(irq, bitmasklist) except: pass elif set_affinity_warning: set_affinity_warning(pid, new_affinity) else: print("move_threads_to_cpu: %s " % \ (_("could not change %(pid)d affinity to %(new_affinity)s") % \ {'pid':pid, 'new_affinity':new_affinity})) # See if this is the thread group leader if pid not in ps: continue threads = procfs.pidstats(f"/proc/{pid}/task") for tid in list(threads.keys()): try: curr_affinity = os.sched_getaffinity(tid) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err if set(curr_affinity) != set(new_affinity): try: os.sched_setaffinity(tid, new_affinity) curr_affinity = os.sched_getaffinity(tid) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err if set(curr_affinity) == set(new_affinity): changed = True elif set_affinity_warning: set_affinity_warning(tid, new_affinity) else: print("move_threads_to_cpu: %s " % \ (_("could not change %(pid)d affinity to %(new_affinity)s") % \ {'pid':pid, 'new_affinity':new_affinity})) except OSError as err: if err.args[0] == errno.ESRCH: # process died continue if err.args[0] == errno.EINVAL: # unmovable thread) print("thread %(pid)d cannot be moved as requested" %{'pid':pid}, file=sys.stderr) continue raise err return changed def move_irqs_to_cpu(cpus, irq_list, spread=False): changed = 0 unprocessed = [] cpu_idx = 0 nr_cpus = len(cpus) new_affinity = cpus last_cpu = max(cpus) + 1 irqs = None ps = procfs.pidstats() for i in irq_list: try: irq = int(i) except: if not irqs: irqs = procfs.interrupts() irq = irqs.find_by_user(i) if not irq: unprocessed.append(i) continue try: irq = int(irq) except: unprocessed.append(i) continue if spread: new_affinity = [cpus[cpu_idx]] cpu_idx += 1 if cpu_idx == nr_cpus: cpu_idx = 0 bitmasklist = procfs.hexbitmask(new_affinity, last_cpu) set_irq_affinity(irq, bitmasklist) changed += 1 pid = ps.find_by_name("IRQ-%d" % irq) if pid: pid = int(pid[0]) try: os.sched_setaffinity(pid, new_affinity) except OSError as err: if err.args[0] == errno.ESRCH: unprocessed.append(i) changed -= 1 continue raise err return (changed, unprocessed) def affinity_remove_cpus(affinity, cpus, nr_cpus): # If the cpu being isolated was the only one in the current affinity affinity = list(set(affinity) - set(cpus)) if not affinity: affinity = list(range(nr_cpus)) affinity = list(set(affinity) - set(cpus)) return affinity # Should be moved to python_linux_procfs.interrupts, shared with interrupts.parse_affinity, etc. def parse_irq_affinity_filename(filename, nr_cpus): try: f = open(f"/proc/irq/{filename}") except IOError as err: if procfs.is_s390(): print("This operation is not supported on s390", file=sys.stderr) print(f"tuna: {err}", file=sys.stderr) sys.exit(2) line = f.readline() f.close() return utilist.bitmasklist(line, nr_cpus) def isolate_cpus(cpus, nr_cpus): fname = sys._getframe().f_code.co_name # Function name ps = procfs.pidstats() ps.reload_threads() previous_pid_affinities = {} for pid in list(ps.keys()): if procfs.cannot_set_affinity(ps, pid): continue try: affinity = os.sched_getaffinity(pid) except OSError as err: if err.args[0] == errno.ESRCH: continue if err.args[0] == errno.EINVAL: print("Function:", fname, ",", err.strerror, file=sys.stderr) sys.exit(2) raise err if set(affinity).intersection(set(cpus)): previous_pid_affinities[pid] = copy.copy(affinity) affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) try: os.sched_setaffinity(pid, affinity) except OSError as err: if err.args[0] == errno.ESRCH: continue if err.args[0] == errno.EINVAL: print("Function:", fname, ",", err.strerror, file=sys.stderr) sys.exit(2) if err.args[0] == errno.EBUSY: comm = ps[pid].stat["comm"] print(f'Warning: Unable to isolate pid {pid} [{comm}]') continue raise err if "threads" not in ps[pid]: continue threads = ps[pid]["threads"] for tid in list(threads.keys()): if procfs.cannot_set_thread_affinity(ps, pid, tid): continue try: affinity = os.sched_getaffinity(tid) except OSError as err: if err.args[0] == errno.ESRCH: continue if err.args[0] == errno.EINVAL: print("Function:", fname, ",", err.strerror, file=sys.stderr) sys.exit(2) raise err if set(affinity).intersection(set(cpus)): previous_pid_affinities[tid] = copy.copy(affinity) affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) try: os.sched_setaffinity(tid, affinity) except OSError as err: if err.args[0] == errno.ESRCH: continue if err.args[0] == errno.EINVAL: print("Function:", fname, ",", err.strerror, file=sys.stderr) sys.exit(2) raise err del ps # Now isolate it from IRQs too irqs = procfs.interrupts() previous_irq_affinities = {} for irq in list(irqs.keys()): # LOC, NMI, TLB, etc if "affinity" not in irqs[irq]: continue affinity = irqs[irq]["affinity"] if set(affinity).intersection(set(cpus)): previous_irq_affinities[irq] = copy.copy(affinity) affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) set_irq_affinity(int(irq), procfs.hexbitmask(affinity, nr_cpus)) affinity = parse_irq_affinity_filename("default_smp_affinity", nr_cpus) affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) set_irq_affinity_filename("default_smp_affinity", procfs.hexbitmask(affinity, nr_cpus)) return (previous_pid_affinities, previous_irq_affinities) def include_cpus(cpus, nr_cpus): ps = procfs.pidstats() ps.reload_threads() previous_pid_affinities = {} for pid in list(ps.keys()): if procfs.cannot_set_affinity(ps, pid): continue try: affinity = os.sched_getaffinity(pid) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err if affinity.intersection(set(cpus)) != set(cpus): previous_pid_affinities[pid] = copy.copy(affinity) affinity = list(affinity) + cpus try: os.sched_setaffinity(pid, affinity) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err if "threads" not in ps[pid]: continue threads = ps[pid]["threads"] for tid in list(threads.keys()): if procfs.cannot_set_thread_affinity(ps, pid, tid): continue try: affinity = os.sched_getaffinity(tid) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err if affinity.intersection(set(cpus)) != set(cpus): previous_pid_affinities[tid] = copy.copy(affinity) affinity = list(affinity) + cpus try: os.sched_setaffinity(tid, affinity) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err del ps # Now include it in IRQs too irqs = procfs.interrupts() previous_irq_affinities = {} for irq in list(irqs.keys()): # LOC, NMI, TLB, etc if "affinity" not in irqs[irq]: continue affinity = irqs[irq]["affinity"] if set(affinity).intersection(set(cpus)) != set(cpus): previous_irq_affinities[irq] = copy.copy(affinity) affinity = list(set(affinity + cpus)) set_irq_affinity(int(irq), procfs.hexbitmask(affinity, nr_cpus)) affinity = parse_irq_affinity_filename("default_smp_affinity", nr_cpus) affinity = list(set(affinity + cpus)) set_irq_affinity_filename("default_smp_affinity", procfs.hexbitmask(affinity, nr_cpus)) return (previous_pid_affinities, previous_irq_affinities) def get_irq_users(irqs, irq, nics=None): if not nics: nics = ethtool.get_active_devices() users = irqs[irq]["users"] for u in users: if u in nics: try: users[users.index(u)] = "%s(%s)" % (u, ethtool.get_module(u)) except IOError: # Old kernel, doesn't implement ETHTOOL_GDRVINFO pass return users def get_irq_affinity_text(irqs, irq): affinity_list = irqs[irq]["affinity"] try: return list_to_cpustring(affinity_list) except: # needs root prio to read /proc/irq//smp_affinity return "" def get_policy_and_rtprio(parm): parms = parm.split(":") rtprio = 0 policy = None try: cp = tuna_sched.Policy(parms[0]) except: if parms[0].isdigit(): rtprio = int(parms[0]) else: raise ValueError else: policy = cp.get_policy() if len(parms) > 1: rtprio = int(parms[1]) elif cp.is_rt(): rtprio = 1 return (policy, rtprio) def thread_filtered(tid, cpus_filtered, show_kthreads, show_uthreads): if cpus_filtered: try: affinity = os.sched_getaffinity(tid) except OSError as err: if err.args[0] == errno.ESRCH: return False raise err if set(cpus_filtered + list(affinity)) == set(cpus_filtered): return True if not (show_kthreads and show_uthreads): kthread = iskthread(tid) if ((not show_kthreads) and kthread) or \ ((not show_uthreads) and not kthread): return True return False def irq_filtered(irq, irqs, cpus_filtered, is_root): if cpus_filtered and is_root: affinity = irqs[irq]["affinity"] if set(cpus_filtered + affinity) == set(cpus_filtered): return True return False def thread_set_priority(tid, policy, rtprio): param = os.sched_param(rtprio) if not policy and policy != 0: policy = os.sched_getscheduler(tid) os.sched_setscheduler(tid, policy, param) def threads_set_priority(tids, parm, affect_children=False): (policy, rtprio) = parm for tid in tids: try: thread_set_priority(tid, policy, rtprio) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err if affect_children: for child in [int(a) for a in os.listdir("/proc/%d/task" % tid)]: if child != tid: try: thread_set_priority(child, policy, rtprio) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err class sched_tunings: def __init__(self, name, pid, policy, rtprio, affinity, percpu): self.name = name self.pid = pid self.policy = policy self.rtprio = int(rtprio) self.affinity = affinity self.percpu = percpu def get_kthread_sched_tunings(proc=None): if not proc: proc = procfs.pidstats() kthreads = {} for pid in list(proc.keys()): name = proc[pid]["stat"]["comm"] # Trying to set the priority of the migration threads will # fail, at least on 3.6.0-rc1 and doesn't make sense anyway # and this function is only used to save those priorities # to reset them using tools like rtctl, skip those to # avoid sched_setscheduler/chrt to fail if iskthread(pid) and not name.startswith("migration/"): rtprio = int(proc[pid]["stat"]["rt_priority"]) try: policy = os.sched_getscheduler(pid) affinity = os.sched_getaffinity(pid) except OSError as err: if err.args[0] == errno.ESRCH: continue raise err percpu = iskthread(pid) and \ proc.is_bound_to_cpu(pid) kthreads[name] = sched_tunings(name, pid, policy, rtprio, affinity, percpu) return kthreads def run_command(cmd, policy, rtprio, cpu_list, background): newpid = os.fork() if newpid == 0: cmd_list = shlex.split(cmd) pid = os.getpid() if rtprio: try: thread_set_priority(pid, policy, rtprio) except (SystemError, OSError) as err: print(f"tuna: {err}") sys.exit(2) if cpu_list: try: os.sched_setaffinity(pid, cpu_list) except (SystemError, OSError) as err: print(f"tuna: {err}") sys.exit(2) try: os.execvp(cmd_list[0], cmd_list) except (SystemError, OSError) as err: print(f"tuna: {err}") sys.exit(2) else: if not background: os.waitpid(newpid, 0) def generate_rtgroups(filename, kthreads, nr_cpus): f = open(filename, "w") f.write('''# Generated by tuna # # Use it with rtctl: # # rtctl --file %s reset # # Please use 'man rtctl' for more operations # # Associate processes into named groups with default priority and # scheduling policy. # # Format is: ::: # # groupname must start at beginning of line. # sched must be one of: 'f' (fifo) # 'b' (batch) # 'r' (round-robin) # 'o' (other) # '*' (leave alone) # regex is an awk regex # # The regex is matched against process names as printed by "ps -eo cmd". ''' % filename) f.write("kthreads:*:1:*:\[.*\]$\n\n") per_cpu_kthreads = [] names = list(kthreads.keys()) names.sort() for name in names: kt = kthreads[name] try: idx = name.index("/") common = name[:idx] if common in per_cpu_kthreads: continue per_cpu_kthreads.append(common) name = common if common[:5] == "sirq-": common = "(sirq|softirq)" + common[4:] elif common[:8] == "softirq-": common = "(sirq|softirq)" + common[7:] name = "s" + name[4:] regex = common + "\/.*" except: idx = 0 regex = name if kt.percpu or idx != 0 or name == "posix_cpu_timer": # Don't mess with workqueues, etc # posix_cpu_timer is too long and doesn't # have PF_THREAD_BOUND in its per process # flags... mask = "*" else: mask = ",".join([hex(a) for a in \ procfs.hexbitmask(kt.affinity, nr_cpus)]) f.write("%s:%c:%d:%s:\[%s\]$\n" % (name, \ tuna_sched.sched_str(kt.policy)[6].lower(), \ kt.rtprio, mask, regex)) f.close() def nohz_full_list(): return [int(cpu) for cpu in procfs.cmdline().options["nohz_full"].split(",")] tuna-0.19/tuna/tuna_gui.glade000066400000000000000000002350411437350234000162000ustar00rootroot00000000000000 True False 1 Tuna 800 600 True False True True True False 0 True False True True 3 160 True True 3 200 True True True False True True 2 False True True True True True 2 True True True False False True True True True 2 True True True True False Monitoring 1 False True True True False queue True False 2 True True False 0 0 0 0 True False True False Current active tuna profile: True True 0 True False True True 1 True False 0 0 0 0 True False 150 True True True True True False 0 0 True False 2 True False gtk-save False False 0 True False Save Snapshot True False False 1 True False 0 220 True True True True True False 0 0 True False 2 True False gtk-save False False 0 True False Save & Apply permanently True False False 1 True False 1 150 True True True True True False 0 0 True False 2 True False gtk-undo False False 0 True False Restore changes True False False 1 True False 2 220 True True True True True False 0 0 True False 2 True False gtk-apply False False 0 True False Apply changes True False False 1 True False 3 1 2 20 20 1 True False Profile management 1 False True False 0 True False 12 True False True False 0 none True False 12 True False Load Profile from External Location True True False True False False 0 True False 0 none True False True True in True True True True 1 True False 0 none True False True 0 0 True True 2 True True 0 True False 0 none True False 4 True False True False True True True True True True 0 46 True False 34 True True False True False 0 0 True False 2 True False gtk-save False False 0 True False Save Configuration to File True False False 1 True False 0 True True False 34 True False 0 0 True False 2 True False gtk-apply False False 0 True False Update Management Tab True False False 1 True False 1 False True 1 True True 1 2 True False Profile editing 2 False True False 5 GtkFileChooserDialog dialog True False 2 True False end gtk-cancel True True True False True False False 0 gtk-open True True True True False True False False 1 False False end 0 button3 button4 True False Set IRQ Attributes dialog True False True False end gtk-cancel True True True False True False False 0 gtk-ok True True True False True False False 1 False False end 0 True False 0.05000000074505806 True False True False 12 True True True True 0 True False 2 3 10 True True True 2 3 1 2 True True 1 True 1 2 1 2 True False 1 2 True False A_ffinity True 0 2 3 True False _Scheduler priority True 0 1 2 True False _Policy True 0 False True 1 False True 1 cancelbutton2 okbutton2 600 500 True False Set Process Attributes dialog True False True False end gtk-cancel True True True False True False False 0 gtk-ok True True True False True False False 1 False False end 0 True False 1 True False 2 True False 0 True False 5 3 _Just the selected thread True True False True True False False 0 _All threads of the selected process True True False True True just_this_thread False False 1 A_ll command lines matching the regex below: True True False True True just_this_thread False False 2 True True 0 True False 5 5 True False True False _Policy: True False False 0 True False True True 1 False False 0 True False 2 True False _Scheduler priority: True True False False 0 True True 1 True True True 1 False False 1 True False True False A_ffinity: True False False 0 True True True True 1 False False 2 False False 1 False False 0 True False 3 True False 3 Command line rege_x: True True False False 0 True True True True 1 False False 1 True True True True True True 2 False True 1 cancelbutton1 okbutton1 tuna-0.19/tuna/tuna_gui.py000077500000000000000000000142361437350234000155600ustar00rootroot00000000000000# -*- python -*- # -*- coding: utf-8 -*- import sys import os import locale import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject import procfs from .gui.cpuview import cpuview from .gui.irqview import irqview from .gui.procview import procview from .gui.commonview import commonview from .gui.profileview import profileview from .config import Config tuna_glade_dirs = [".", "tuna", "/usr/share/tuna"] tuna_glade = None class main_gui: def __init__(self, show_kthreads=True, show_uthreads=True, cpus_filtered=[], refresh_time=2500, disable_perf=False): global tuna_glade (app, localedir) = ('tuna', '/usr/share/locale') locale.bindtextdomain(app, localedir) locale.textdomain(app) if self.check_root(): sys.exit(1) for dir in tuna_glade_dirs: tuna_glade = f"{dir}/tuna_gui.glade" if os.access(tuna_glade, os.F_OK): break self.wtree = Gtk.Builder() self.wtree.add_objects_from_file(tuna_glade, ("mainbig_window", "tuna")) #self.wtree = Gtk.glade.XML(tuna_glade, "mainbig_window", "tuna") self.ps = procfs.pidstats() self.irqs = procfs.interrupts() self.window = self.wtree.get_object("mainbig_window") self.procview = procview( self.wtree.get_object("processlist"), self.ps, show_kthreads, show_uthreads, cpus_filtered, tuna_glade, disable_perf) self.irqview = irqview( self.wtree.get_object("irqlist"), self.irqs, self.ps, cpus_filtered, tuna_glade) self.cpuview = cpuview( self.wtree.get_object("vpaned1"), self.wtree.get_object("hpaned2"), self.wtree.get_object("cpuview"), self.procview, self.irqview, cpus_filtered, refresh_time) self.config = Config() self.check_env() self.commonview = commonview() self.commonview.contentTable = self.wtree.get_object("commonTbl") self.commonview.configFileCombo = self.wtree.get_object("profileSelector") self.profileview = profileview() self.profileview.config = self.config self.commonview.config = self.config self.profileview.commonview = self.commonview self.commonview.profileview = self.profileview self.profileview.setWtree(self.wtree) self.profileview.init_default_file() event_handlers = { "on_mainbig_window_delete_event" : self.on_mainbig_window_delete_event, "on_mainbig_window_destroy_event" : self.on_mainbig_window_delete_event, "on_processlist_button_press_event" : self.procview.on_processlist_button_press_event, "on_irqlist_button_press_event" : self.irqview.on_irqlist_button_press_event, "on_loadProfileButton_clicked" : self.profileview.on_loadProfileButton_clicked, "on_SaveButton_clicked" : self.profileview.on_SaveButton_clicked, "on_UpdateButton_clicked" : self.profileview.on_UpdateButton_clicked, "on_applyChanges_clicked" : self.commonview.on_applyChanges_clicked, "on_undoChanges_clicked" : self.commonview.on_undoChanges_clicked, "on_saveSnapshot_clicked" : self.commonview.on_saveSnapshot_clicked, "on_saveTunedChanges_clicked" : self.commonview.on_saveTunedChanges_clicked, "on_profileSelector_changed" : self.commonview.on_profileSelector_changed, "on_profileTree_button_press_event" : self.profileview.on_profileTree_button_press_event } #self.wtree.signal_autoconnect(event_handlers) self.wtree.connect_signals(event_handlers) self.ps.reload_threads() self.show() self.timer = GObject.timeout_add(refresh_time, self.refresh) try: self.icon = Gtk.status_icon_new_from_stock(Gtk.STOCK_PREFERENCES) self.icon.connect("activate", self.on_status_icon_activate) self.icon.connect("popup-menu", self.on_status_icon_popup_menu) except AttributeError: # Old pygtk2 pass pixbuf = self.window.render_icon(Gtk.STOCK_PREFERENCES, Gtk.IconSize.SMALL_TOOLBAR) self.window.set_icon(pixbuf) def on_status_icon_activate(self, status_icon): if self.window.is_active(): self.window.hide() else: self.window.present() def on_status_icon_popup_menu(self, icon, event_button, event_time): menu = Gtk.Menu() quit = Gtk.MenuItem("_Quit") menu.add(quit) quit.connect_object('activate', self.on_mainbig_window_delete_event, icon) quit.show() menu.popup(None, None, None, event_button, event_time) def on_mainbig_window_delete_event(self, obj, event=None): Gtk.main_quit() def show(self): self.cpuview.refresh() self.irqview.show() self.procview.show() def refresh(self): if not self.procview.evlist: # Poll, as we don't have perf self.ps.reload() self.ps.reload_threads() self.procview.show() self.irqview.refresh() return True def check_root(self): if os.getuid() == 0: return False self.binpath = sys.executable.strip(os.path.basename(sys.executable)) os.execv(self.binpath + 'pkexec', [sys.executable] + [self.binpath + 'tuna'] + sys.argv[1:]) return True def check_env(self): if not os.path.exists(self.config.config["root"]): try: os.stat(self.config.config["root"]) except (IOError, OSError): os.mkdir(self.config.config["root"]) if not os.path.exists("/root/.local/share/"): try: os.stat("/root/.local/share/") except (IOError, OSError): os.mkdir("/root/.local/") os.mkdir("/root/.local/share/") def run(self): Gtk.main() tuna-0.19/tuna/tuna_sched.py000066400000000000000000000046461437350234000160630ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright (C) 2022 John Kacur """ Functions to translate a scheduling policy into either a string name or an equivalent integer """ import os SCHED_POLICIES = { "SCHED_OTHER": os.SCHED_OTHER, "SCHED_FIFO": os.SCHED_FIFO, "SCHED_RR": os.SCHED_RR, "SCHED_BATCH": os.SCHED_BATCH, "SCHED_IDLE": os.SCHED_IDLE, "SCHED_DEADLINE": 6 } def sched_fromstr(policy): """ Given a policy as a string, return the equivalent integer """ try: return SCHED_POLICIES[policy] except KeyError as exc: raise OSError('No such policy') from exc def sched_str(policy): """ Given a policy as an integer, return the equivalent string name """ for pstr, pnum in SCHED_POLICIES.items(): if pnum == policy: return pstr return "UNKNOWN" class Policy: """ class to encapsulate some scheduling policies operations """ SCHED_POLICIES = { "SCHED_OTHER": os.SCHED_OTHER, "SCHED_FIFO": os.SCHED_FIFO, "SCHED_RR": os.SCHED_RR, "SCHED_BATCH": os.SCHED_BATCH, "SCHED_IDLE": os.SCHED_IDLE, "SCHED_DEADLINE": 6 } RT_POLICIES = ["SCHED_FIFO", "SCHED_RR"] def __init__(self, policy): """ init the class given a policy as a string can use fifo, FIFO, sched_fifo, SCHED_FIFO, etc """ self.policy = None policy = policy.upper() if policy[:6] != "SCHED_": policy = "SCHED_" + policy if policy not in list(Policy.SCHED_POLICIES): raise OSError self.policy = policy @classmethod def num_init(cls, policy): """ init the class with an integer corresponding to the policy """ for pstr, pnum in cls.SCHED_POLICIES.items(): if policy == pnum: return cls(pstr) return cls("UNKNOWN") def __str__(self): """ return the policy in string format """ return self.policy def get_sched(self): """ return the policy in string format """ return self.policy def get_short_str(self): """ return the policy in string format, without the SCHED_ part """ return self.policy[6:] def get_policy(self): """ return the integer equivlent of the policy """ return SCHED_POLICIES[self.policy] def is_rt(self): """ Return True if policy is a realtime policy """ return self.policy in Policy.RT_POLICIES