• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdesdk API Reference
  • KDE Home
  • Contact Us
 

kcachegrind

  • sources
  • kde-4.12
  • kdesdk
  • kcachegrind
  • libcore
costitem.cpp
Go to the documentation of this file.
1 /* This file is part of KCachegrind.
2  Copyright (C) 2002 - 2009 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
3 
4  KCachegrind is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public
6  License as published by the Free Software Foundation, version 2.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; see the file COPYING. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 */
18 
19 #include "costitem.h"
20 
21 #include "tracedata.h"
22 
23 #define TRACE_DEBUG 0
24 #define TRACE_ASSERTIONS 0
25 
26 //---------------------------------------------------
27 // ProfileCost
28 
29 CostItem::CostItem(ProfileContext* c)
30 {
31  _position = 0;
32  _dep = 0;
33  _dirty = true;
34 
35  _context = c;
36 }
37 
38 CostItem::~CostItem()
39 {}
40 
41 
42 void CostItem::clear()
43 {
44  invalidate();
45 }
46 
47 
48 QString CostItem::costString(EventTypeSet*)
49 {
50  return QString("(no cost)");
51 }
52 
53 QString CostItem::name() const
54 {
55  if (part()) {
56  return QObject::tr("%1 from %2").arg(_dep->name()).arg(part()->name());
57  }
58 
59  if (_dep)
60  return _dep->name();
61 
62  return QObject::tr("(unknown)");
63 }
64 
65 QString CostItem::prettyName() const
66 {
67  if (name().isEmpty()) return QObject::tr("(unknown)");
68  return name();
69 }
70 
71 QString CostItem::formattedName() const
72 {
73  return QString();
74 }
75 
76 QString CostItem::fullName() const
77 {
78  return QString("%1 %2")
79  .arg(ProfileContext::typeName(type())).arg(prettyName());
80 }
81 
82 QString CostItem::toString()
83 {
84  return QString("%1\n [%3]").arg(fullName()).arg(costString(0));
85 }
86 
87 void CostItem::invalidate()
88 {
89  if (_dirty) return;
90  _dirty = true;
91 
92  if (_dep)
93  _dep->invalidate();
94 }
95 
96 void CostItem::update()
97 {
98  _dirty = false;
99 }
100 
101 TracePart* CostItem::part()
102 {
103  return _position ? _position->part() : 0;
104 }
105 
106 const TracePart* CostItem::part() const
107 {
108  return _position ? _position->part() : 0;
109 }
110 
111 TraceData* CostItem::data()
112 {
113  return _position ? _position->data() : 0;
114 }
115 
116 const TraceData* CostItem::data() const
117 {
118  return _position ? _position->data() : 0;
119 }
120 
121 
122 //---------------------------------------------------
123 // ProfileCostArray
124 
125 
126 const int ProfileCostArray::MaxRealIndex = MaxRealIndexValue;
127 const int ProfileCostArray::InvalidIndex = -1;
128 
129 
130 ProfileCostArray::ProfileCostArray(ProfileContext* context)
131  : CostItem(context)
132 {
133  _cachedType = 0; // no virtual value cached
134  _allocCount = 0;
135  _count = 0;
136  _cost = 0;
137 }
138 
139 ProfileCostArray::ProfileCostArray()
140  : CostItem(ProfileContext::context(ProfileContext::UnknownType))
141 {
142  _cachedType = 0; // no virtual value cached
143  _allocCount = 0;
144  _count = 0;
145  _cost = 0;
146 }
147 
148 ProfileCostArray::~ProfileCostArray()
149 {
150  if (_cost) delete[] _cost;
151 }
152 
153 
154 void ProfileCostArray::clear()
155 {
156  _count = 0;
157  invalidate();
158 }
159 
160 void ProfileCostArray::reserve(int count)
161 {
162  if (count <= _allocCount) return;
163 
164  SubCost* newcost = new SubCost[count];
165  if (_cost) {
166  /* first _count values are valid and have to be preserved */
167  for(int i=0; i<_count; i++)
168  newcost[i] = _cost[i];
169  delete[] _cost;
170  }
171  _cost = newcost;
172  _allocCount = count;
173 }
174 
175 void ProfileCostArray::set(EventTypeMapping* mapping, const char* s)
176 {
177  if (!mapping) return;
178  if (!s) {
179  clear();
180  return;
181  }
182 
183  reserve(mapping->set()->realCount());
184 
185  while(*s == ' ') s++;
186 
187  if (mapping->isIdentity()) {
188  int i = 0;
189  while(i<mapping->count()) {
190  if (!_cost[i].set(&s)) break;
191  i++;
192  }
193  _count = i;
194  }
195  else {
196  int i = 0, maxIndex = 0, index;
197  while(1) {
198  index = mapping->realIndex(i);
199  if (maxIndex<index) maxIndex=index;
200  if (index == ProfileCostArray::InvalidIndex) break;
201  if (!_cost[index].set(&s)) break;
202  i++;
203  }
204  // we have to set all costs of unused indexes till maxIndex to zero
205  for(i=mapping->firstUnused(); i<=maxIndex; i=mapping->nextUnused(i))
206  _cost[i] = 0;
207  _count = maxIndex;
208  }
209  Q_ASSERT(_count <= _allocCount);
210  // a cost change has to be propagated (esp. in subclasses)
211  invalidate();
212 }
213 
214 void ProfileCostArray::set(EventTypeMapping* mapping, FixString & s)
215 {
216  if (!mapping) return;
217  s.stripSpaces();
218  if (s.isEmpty()) {
219  clear();
220  return;
221  }
222 
223  reserve(mapping->set()->realCount());
224 
225  if (mapping->isIdentity()) {
226  int i = 0;
227  while(i<mapping->count()) {
228  if (!s.stripUInt64(_cost[i])) break;
229  i++;
230  }
231  _count = i;
232  }
233  else {
234  int i = 0, maxIndex = 0, index;
235  while(1) {
236  index = mapping->realIndex(i);
237  if (maxIndex<index) maxIndex=index;
238  if (index == ProfileCostArray::InvalidIndex) break;
239  if (!s.stripUInt64(_cost[index])) break;
240  i++;
241  }
242  // we have to set all costs of unused indexes till maxIndex to zero
243  for(i=mapping->firstUnused(); i<=maxIndex; i=mapping->nextUnused(i))
244  _cost[i] = 0;
245  _count = maxIndex+1;
246  }
247  Q_ASSERT(_count <= _allocCount);
248  invalidate();
249 }
250 
251 
252 void ProfileCostArray::addCost(EventTypeMapping* mapping, const char* s)
253 {
254  if (!mapping || !s) return;
255  reserve(mapping->set()->realCount());
256 
257  SubCost v;
258  if (mapping->isIdentity()) {
259  int i = 0;
260  while(i<mapping->count()) {
261  if (!v.set(&s)) break;
262  if (i<_count)
263  _cost[i] += v;
264  else
265  _cost[i] = v;
266  i++;
267  }
268  if (i > _count) _count = i;
269  }
270  else {
271  int i = 0, maxIndex = 0, index;
272  while(1) {
273  if (!v.set(&s)) break;
274  index = mapping->realIndex(i);
275  if (maxIndex<index) maxIndex=index;
276  if (index == ProfileCostArray::InvalidIndex) break;
277  if (index<_count)
278  _cost[index] += v;
279  else
280  _cost[index] = v;
281  i++;
282  }
283  if (maxIndex >= _count) {
284  /* we have to set all costs of unused indexes in the interval
285  * [_count;maxIndex] to zero */
286  for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
287  _cost[i] = 0;
288  _count = maxIndex+1;
289  }
290  }
291 
292  Q_ASSERT(_count <= _allocCount);
293  invalidate();
294 
295 #if TRACE_DEBUG
296  _dirty = false; // do not recurse !
297  qDebug("%s\n now %s", qPrintable( fullName() ),
298  qPrintable( ProfileCostArray::costString(0) ));
299  _dirty = true; // because of invalidate()
300 #endif
301 }
302 
303 void ProfileCostArray::addCost(EventTypeMapping* mapping, FixString & s)
304 {
305  if (!mapping) return;
306  s.stripSpaces();
307  if (s.isEmpty()) return;
308  reserve(mapping->set()->realCount());
309 
310  SubCost v;
311  if (mapping->isIdentity()) {
312  int i = 0;
313  while(i<mapping->count()) {
314  if (!s.stripUInt64(v)) break;
315  if (i<_count)
316  _cost[i] += v;
317  else
318  _cost[i] = v;
319  i++;
320  }
321  if (i > _count) _count = i;
322  }
323  else {
324  int i = 0, maxIndex = 0, index;
325  while(1) {
326  if (!s.stripUInt64(v)) break;
327  index = mapping->realIndex(i);
328  if (maxIndex<index) maxIndex=index;
329  if (index == ProfileCostArray::InvalidIndex) break;
330  if (index<_count)
331  _cost[index] += v;
332  else
333  _cost[index] = v;
334  i++;
335  }
336  if (maxIndex >= _count) {
337  /* we have to set all costs of unused indexes in the interval
338  * [_count;maxIndex] to zero */
339  for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
340  _cost[i] = 0;
341  _count = maxIndex+1;
342  }
343  }
344 
345  Q_ASSERT(_count <= _allocCount);
346  invalidate();
347 
348 #if TRACE_DEBUG
349  _dirty = false; // do not recurse !
350  qDebug("%s\n now %s", qPrintable( fullName() ),
351  qPrintable( ProfileCostArray::costString(0 ) ) );
352  _dirty = true; // because of invalidate()
353 #endif
354 }
355 
356 
357 // update each subcost to be maximum of old and given costs
358 void ProfileCostArray::maxCost(EventTypeMapping* mapping, FixString & s)
359 {
360  if (!mapping) return;
361  s.stripSpaces();
362  if (s.isEmpty()) return;
363  reserve(mapping->set()->realCount());
364 
365  SubCost v;
366  if (mapping->isIdentity()) {
367  int i = 0;
368  while(i<mapping->count()) {
369  if (!s.stripUInt64(v)) break;
370  if (i<_count) {
371  if (v>_cost[i]) _cost[i] = v;
372  }
373  else
374  _cost[i] = v;
375  i++;
376  }
377  if (i > _count) _count = i;
378  }
379  else {
380  int i = 0, maxIndex = 0, index;
381  while(1) {
382  if (!s.stripUInt64(v)) break;
383  index = mapping->realIndex(i);
384  if (maxIndex<index) maxIndex=index;
385  if (index == ProfileCostArray::InvalidIndex) break;
386  if (index<_count) {
387  if (v>_cost[index]) _cost[index] = v;
388  }
389  else
390  _cost[index] = v;
391  i++;
392  }
393  if (maxIndex >= _count) {
394  /* we have to set all costs of unused indexes in the interval
395  * [_count;maxIndex] to zero */
396  for(i=mapping->nextUnused(_count-1); i<=maxIndex; i=mapping->nextUnused(i))
397  _cost[i] = 0;
398  _count = maxIndex+1;
399  }
400  }
401 
402  Q_ASSERT(_count <= _allocCount);
403  invalidate();
404 
405 #if TRACE_DEBUG
406  _dirty = false; // do not recurse !
407  qDebug("%s\n now %s", qPrintable( fullName() ),
408  qPrintable(ProfileCostArray::costString(0)));
409  _dirty = true; // because of invalidate()
410 #endif
411 }
412 
413 
414 void ProfileCostArray::addCost(ProfileCostArray* item)
415 {
416  int i;
417  if (!item) return;
418 
419  // we have to update the other item if needed
420  // because we access the item costs directly
421  if (item->_dirty) item->update();
422 
423  // make sure we have enough space allocated
424  reserve(item->_count);
425 
426  if (item->_count < _count) {
427  for (i = 0; i<item->_count; i++)
428  _cost[i] += item->_cost[i];
429  }
430  else {
431  for (i = 0; i<_count; i++)
432  _cost[i] += item->_cost[i];
433  for (; i<item->_count; i++)
434  _cost[i] = item->_cost[i];
435  _count = item->_count;
436  }
437 
438  Q_ASSERT(_count <= _allocCount);
439  invalidate();
440 
441 #if TRACE_DEBUG
442  _dirty = false; // do not recurse !
443  qDebug("%s added cost item\n %s\n now %s",
444  qPrintable( fullName() ), qPrintable(item->fullName()),
445  qPrintable(ProfileCostArray::costString(0)));
446  _dirty = true; // because of invalidate()
447 #endif
448 }
449 
450 void ProfileCostArray::maxCost(ProfileCostArray* item)
451 {
452  int i;
453 
454  if (!item) return;
455 
456  // we have to update the other item if needed
457  // because we access the item costs directly
458  if (item->_dirty) item->update();
459 
460  // make sure we have enough space allocated
461  reserve(item->_count);
462 
463  if (item->_count < _count) {
464  for (i = 0; i<item->_count; i++)
465  if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
466  }
467  else {
468  for (i = 0; i<_count; i++)
469  if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
470  for (; i<item->_count; i++)
471  _cost[i] = item->_cost[i];
472  _count = item->_count;
473  }
474 
475  Q_ASSERT(_count <= _allocCount);
476  invalidate();
477 
478 #if TRACE_DEBUG
479  _dirty = false; // do not recurse !
480  qDebug("%s added cost item\n %s\n now %s",
481  qPrintable( fullName() ), qPrintable(item->fullName()),
482  qPrintable( ProfileCostArray::costString(0) ));
483  _dirty = true; // because of invalidate()
484 #endif
485 }
486 
487 void ProfileCostArray::addCost(int realIndex, SubCost value)
488 {
489  if (realIndex<0 || realIndex>=MaxRealIndex) return;
490  if (value == 0) return;
491 
492  reserve(realIndex+1);
493  if (realIndex < _count)
494  _cost[realIndex] += value;
495  else {
496  for(int i=_count;i<realIndex;i++)
497  _cost[i] = 0;
498  _cost[realIndex] = value;
499  _count = realIndex+1;
500  }
501 
502  Q_ASSERT(_count <= _allocCount);
503  invalidate();
504 }
505 
506 void ProfileCostArray::maxCost(int realIndex, SubCost value)
507 {
508  if (realIndex<0 || realIndex>=MaxRealIndex) return;
509 
510  reserve(realIndex+1);
511  if (realIndex<_count) {
512  if (value>_cost[realIndex]) _cost[realIndex] = value;
513  }
514  else {
515  for(int i=_count;i<realIndex;i++)
516  _cost[i] = 0;
517  _cost[realIndex] = value;
518  _count = realIndex+1;
519  }
520 
521  Q_ASSERT(_count <= _allocCount);
522  invalidate();
523 }
524 
525 
526 ProfileCostArray ProfileCostArray::diff(ProfileCostArray* item)
527 {
528  ProfileCostArray res(context());
529 
530  // we have to update the other item if needed
531  // because we access the item costs directly
532  if (item->_dirty) item->update();
533 
534  int maxCount = (item->_count > _count) ? item->_count : _count;
535 
536  res.reserve(maxCount);
537  for (int i=0; i<maxCount;i++)
538  res._cost[i] = item->subCost(i) - subCost(i);
539  res._count = maxCount;
540 
541  Q_ASSERT(res._count <= res._allocCount);
542  return res;
543 }
544 
545 QString ProfileCostArray::costString(EventTypeSet* set)
546 {
547  QString res;
548 
549  if (_dirty) update();
550 
551  int maxIndex = set ? set->realCount() : ProfileCostArray::MaxRealIndex;
552  for (int i = 0; i<maxIndex; i++) {
553  if (!res.isEmpty()) res += ", ";
554  if (set) res += set->type(i)->name() + ' ';
555 
556  res += subCost(i).pretty();
557  }
558  return res;
559 }
560 
561 
562 void ProfileCostArray::invalidate()
563 {
564  if (_dirty) return;
565  _dirty = true;
566  _cachedType = 0; // cached value is invalid, too
567 
568  if (_dep)
569  _dep->invalidate();
570 }
571 
572 void ProfileCostArray::update()
573 {
574  _dirty = false;
575 }
576 
577 // this is only for real types
578 SubCost ProfileCostArray::subCost(int idx)
579 {
580  if (idx<0) return 0;
581 
582  /* update if needed as cost could be calculated dynamically in subclasses
583  * this can change _count !! */
584  if (_dirty) update();
585  if (idx>=_count) return 0;
586 
587  return _cost[idx];
588 }
589 
590 
591 SubCost ProfileCostArray::subCost(EventType* t)
592 {
593  if (!t) return 0;
594  if (_cachedType != t) {
595  _cachedType = t;
596  _cachedCost = t->subCost(this);
597  }
598  return _cachedCost;
599 }
600 
601 QString ProfileCostArray::prettySubCost(EventType* t)
602 {
603  return subCost(t).pretty();
604 }
605 
606 QString ProfileCostArray::prettySubCostPerCall(EventType* t, int calls)
607 {
608  if (calls == 0) {
609  /* For callgrind, a call count of zero means that
610  * a function was already active when measuring started
611  * (even without that possibility, we never should crash).
612  * To show full cost, set <calls> to 1.
613  */
614  calls = 1;
615  }
616  return SubCost(subCost(t) / calls).pretty();
617 }
618 
EventTypeMapping::firstUnused
int firstUnused()
Allows an iteration over the sorted list of all real indexes not used in this mapping, up to topIndex (use ProfileCostArray::MaxRealIndex for all).
Definition: eventtype.h:204
ProfileCostArray::reserve
void reserve(int)
Definition: costitem.cpp:160
CostItem::part
virtual TracePart * part()
Definition: costitem.cpp:101
EventTypeSet::realCount
int realCount()
Definition: eventtype.h:133
TracePart::name
QString name() const
Definition: tracedata.h:667
ProfileCostArray::subCost
SubCost subCost(EventType *)
Returns a sub cost.
Definition: costitem.cpp:591
FixString::stripSpaces
void stripSpaces()
Strip leading spaces.
Definition: utils.cpp:155
CostItem::_dep
CostItem * _dep
Definition: costitem.h:127
CostItem::costString
virtual QString costString(EventTypeSet *)
Returns text of all cost metrics.
Definition: costitem.cpp:48
ProfileCostArray::MaxRealIndex
static const int MaxRealIndex
Definition: costitem.h:150
ProfileContext::typeName
static QString typeName(Type)
Definition: context.cpp:62
EventTypeMapping::nextUnused
int nextUnused(int i)
Definition: eventtype.h:205
CostItem::update
virtual void update()
Updates cost attributes.
Definition: costitem.cpp:96
CostItem::type
ProfileContext::Type type() const
Definition: costitem.h:45
MaxRealIndexValue
#define MaxRealIndexValue
Definition: costitem.h:136
ProfileCostArray::prettySubCost
QString prettySubCost(EventType *)
Returns a cost attribute converted to a string (with space after every 3 digits)
Definition: costitem.cpp:601
CostItem::~CostItem
virtual ~CostItem()
Definition: costitem.cpp:38
ProfileCostArray::invalidate
virtual void invalidate()
Invalidate the cost attributes.
Definition: costitem.cpp:562
CostItem::CostItem
CostItem(ProfileContext *)
Definition: costitem.cpp:29
CostItem
Base class for cost items.
Definition: costitem.h:37
ProfileCostArray::InvalidIndex
static const int InvalidIndex
Definition: costitem.h:151
ProfileCostArray::set
void set(EventTypeMapping *, const char *)
Definition: costitem.cpp:175
EventTypeSet::type
EventType * type(int)
Definition: eventtype.cpp:475
ProfileCostArray::costString
virtual QString costString(EventTypeSet *)
Returns text of all cost metrics.
Definition: costitem.cpp:545
EventTypeMapping
A index list into a EventTypeSet.
Definition: eventtype.h:170
EventType
A cost type, e.g.
Definition: eventtype.h:43
ProfileCostArray::update
virtual void update()
Updates cost attributes.
Definition: costitem.cpp:572
CostItem::formattedName
virtual QString formattedName() const
A HTMLified version of name, can return empty string.
Definition: costitem.cpp:71
ProfileCostArray::~ProfileCostArray
virtual ~ProfileCostArray()
Definition: costitem.cpp:148
ProfileCostArray::maxCost
void maxCost(EventTypeMapping *, FixString &)
Definition: costitem.cpp:358
EventTypeMapping::set
EventTypeSet * set()
Definition: eventtype.h:179
tracedata.h
ProfileCostArray
An array of basic cost metrics for a trace item.
Definition: costitem.h:144
EventTypeMapping::realIndex
int realIndex(int i)
Definition: eventtype.h:191
CostItem::clear
virtual void clear()
Set all cost counters to zero.
Definition: costitem.cpp:42
ProfileContext
Base class for source contexts which event costs contained in a ProfileData instance, ie.
Definition: context.h:32
costitem.h
CostItem::name
virtual QString name() const
Returns dynamic name info (without type)
Definition: costitem.cpp:53
CostItem::fullName
QString fullName() const
Returns type name + dynamic name.
Definition: costitem.cpp:76
CostItem::context
ProfileContext * context() const
Definition: costitem.h:44
CostItem::data
virtual TraceData * data()
Definition: costitem.cpp:111
FixString
A simple, constant string class.
Definition: utils.h:38
CostItem::invalidate
virtual void invalidate()
Invalidate the cost attributes.
Definition: costitem.cpp:87
EventType::subCost
SubCost subCost(ProfileCostArray *)
Definition: eventtype.cpp:196
ProfileCostArray::prettySubCostPerCall
QString prettySubCostPerCall(EventType *t, int calls)
Definition: costitem.cpp:606
TracePart
A Trace Part: All data read from a trace file, containing all costs that happened in a specified time...
Definition: tracedata.h:655
ProfileCostArray::clear
virtual void clear()
Set all cost counters to zero.
Definition: costitem.cpp:154
CostItem::_context
ProfileContext * _context
Definition: costitem.h:123
FixString::isEmpty
bool isEmpty()
Definition: utils.h:53
CostItem::_dirty
bool _dirty
Definition: costitem.h:124
CostItem::_position
CostItem * _position
Definition: costitem.h:126
SubCost
Cost event counter, simple wrapper around a 64bit entity.
Definition: subcost.h:32
ProfileCostArray::addCost
void addCost(EventTypeMapping *, const char *)
Definition: costitem.cpp:252
FixString::stripUInt64
bool stripUInt64(uint64 &, bool stripSpaces=true)
Definition: utils.cpp:210
SubCost::pretty
QString pretty(char sep= ' ') const
Convert SubCost value into a QString, spaced every 3 digits.
Definition: subcost.cpp:46
EventTypeSet
A class for managing a set of event types.
Definition: eventtype.h:117
EventTypeMapping::isIdentity
bool isIdentity()
Is this mapping the identity( i.e.
Definition: eventtype.h:190
CostItem::toString
QString toString()
Returns full name + cost text.
Definition: costitem.cpp:82
TraceData
This class holds profiling data of multiple tracefiles generated with cachegrind on one command...
Definition: tracedata.h:1363
CostItem::prettyName
virtual QString prettyName() const
Similar to name, but prettyfied = more descriptive to humans.
Definition: costitem.cpp:65
ProfileCostArray::ProfileCostArray
ProfileCostArray()
Definition: costitem.cpp:139
ProfileCostArray::diff
ProfileCostArray diff(ProfileCostArray *item)
Definition: costitem.cpp:526
EventType::name
const QString & name()
Definition: eventtype.h:65
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:03:27 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kcachegrind

Skip menu "kcachegrind"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdesdk API Reference

Skip menu "kdesdk API Reference"
  • kapptemplate
  • kcachegrind
  • kompare
  • lokalize
  • okteta
  • umbrello
  •   umbrello

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal